Compare commits

...

22 Commits

Author SHA1 Message Date
26ce3260b9 fix: load yaml config file 2024-05-02 08:46:36 +02:00
8dc7bbc6d4 fix: remove app yml 2024-04-29 22:18:22 +02:00
091ad1fec0 feat: update README 2024-04-29 21:52:15 +02:00
ad77710ad1 fix: Dockerfile 2024-04-29 21:50:35 +02:00
5860f81aa2 refactor: change configruation to yml 2024-04-29 21:50:16 +02:00
727b083e52 refactor: project structure 2024-04-29 21:29:05 +02:00
2abed91918 feat: update go version 2024-04-29 21:20:06 +02:00
fe002edab1 fix: readme lint 2024-04-29 21:19:03 +02:00
422fedf9cd fix: test 2023-03-09 13:28:27 +01:00
1b83793f38 feat: get envfile from path when is not prod 2023-03-03 22:47:44 +01:00
f56e85cc64 feat: use env path as parameter 2023-03-03 22:46:49 +01:00
4877f72f04 feat: config small fix 2023-02-26 22:10:07 +01:00
60b40c84f3 feat: upgrade makefile 2023-02-26 22:09:51 +01:00
c0daf06ee6 feature: update readme 2023-02-26 19:00:02 +01:00
41517ff292 feature: add license 2023-02-26 18:41:05 +01:00
16b0a31dd4 feat: complete tests 2023-02-26 16:59:20 +01:00
49a72dc34b feat: log event 2023-02-26 16:26:13 +01:00
a67ef6f2d8 feat: block routine with for loop 2023-02-26 16:19:12 +01:00
19f17308b1 feat: test pkg watcher 2023-02-26 16:07:14 +01:00
e25b192aff feat: update makefile 2023-02-26 11:07:30 +01:00
ca4541ca1a feat: update gitignore 2023-02-26 11:07:25 +01:00
e02c244425 feat: update README 2023-02-26 11:07:21 +01:00
19 changed files with 287 additions and 115 deletions

View File

@@ -1,4 +0,0 @@
SCRIPT_BINARY_PATH=
WEBHOOK_SCRIPT_PATH=
FILE_TO_WATCH_PATH=
TEST_FILE_TO_WATCH_PATH=

2
.gitignore vendored
View File

@@ -1,3 +1,5 @@
.env .env
coverage/* coverage/*
test_monitor.txt test_monitor.txt
bin
configs/*

28
Dockerfile Normal file
View File

@@ -0,0 +1,28 @@
FROM golang:1.22 as builder
WORKDIR /app
# Copy the local package files to the container's workspace.
COPY go.mod go.sum ./
RUN go mod download
# Copy the project code into the container
COPY . .
# Build the Go app
RUN CGO_ENABLED=0 GOOS=linux go build -v -o git-webhook-ci
# Use a small Alpine Linux image
FROM alpine:latest
# Set the working directory
WORKDIR /root/
# Copy the binary from the builder stage
COPY --from=builder /app/git-webhook-ci .
# Document that the service listens on port 8080.
EXPOSE 8080
# Run the binary.
CMD ["./git-webhook-ci"]

8
LICENSE Normal file
View File

@@ -0,0 +1,8 @@
---- Definitions ----
license means right to use
Everybody is invited to contribute to improve this project and the main idea.
This idea which is to help the community to develop more secure code.
By the grace of YAHWEH

View File

@@ -1,4 +1,5 @@
BINARY_DIR=bin
BINARY_NAME=webhook-listener
COVERAGE_DIR=coverage COVERAGE_DIR=coverage
lint: lint:
@@ -12,5 +13,7 @@ test-coverage:
mkdir ${COVERAGE_DIR} mkdir ${COVERAGE_DIR}
go test -v -coverprofile ${COVERAGE_DIR}/cover.out ./... go test -v -coverprofile ${COVERAGE_DIR}/cover.out ./...
go tool cover -html ${COVERAGE_DIR}/cover.out -o ${COVERAGE_DIR}/cover.html go tool cover -html ${COVERAGE_DIR}/cover.out -o ${COVERAGE_DIR}/cover.html
cd ${COVERAGE_DIR} build:
open cover.html rm -rf ${BINARY_DIR}
mkdir ${BINARY_DIR}
env GOOS=linux CGO_ENABLED=0 GOARCH=amd64 go build -o ./${BINARY_DIR}/${BINARY_NAME} main.go

View File

@@ -1,18 +1,78 @@
# git-webhook-ci # git-webhook-ci
Tool to automatize your deploy based on file write changes Tool to automatize your deploy based on file write changes
## Description
After some change is made in our listener file the script placed on path set on `.yaml` configuration falled placed on `./configs/app.yaml` variable called **webhook_script_path** will be executed.
## Context ## Context
As a security risk that could be allow a webhook listener on a VPS for your git repository, I've decided As a security risk that could be allow a webhook listener on a VPS for your git repository, I've decided
to create this package which is a listener to file changes on write. This will trigger a bash script which to create this package which is a listener to file changes on write. This will trigger a bash script which
you shold place on your server to run desired tasks. In my case I've done this to run a bash deploy command like you shold place on your server to run desired tasks. In my case I've done this to run a bash deploy command like
- git pull
- build - git pull
- move build to desired dir - build
- restart services like nginx or whatever - move build to desired dir
- restart services like nginx or whatever
## Installation ## Installation
### Requirements ### Requirements
- [Go version > 1.19](https://go.dev/dl/)
- [Go version > 1.22](https://go.dev/dl/)
- [GNU Make 4.3](https://www.gnu.org/software/make/) - [GNU Make 4.3](https://www.gnu.org/software/make/)
- [goreportcard-cli](https://github.com/gojp/goreportcard) - [goreportcard-cli](https://github.com/gojp/goreportcard)
- [golangci-lint](https://golangci-lint.run/) - [golangci-lint](https://golangci-lint.run/)
### Environment Vars
As shown in app.example.yml you have to configure this variables in order to make your binary works right:
```.env
script_binary_path: "/bin/bash"
webhook_script_path: "./test-script.sh"
file_to_watch_path: "./test_monitor.txt"
```
## How to use
### Build
To build you can only just `make build` command
```bash
make build
```
## Tests
```bash
make test-coverage
```
output:
```terminal
coverage: 80.0% of statements
ok gitea.urkob.com/urko/git-webhook-ci/internal/watcher 2.024s coverage: 80.0% of statements
coverage: 100.0% of statements
ok gitea.urkob.com/urko/git-webhook-ci/pkg/watcher 0.017s coverage: 100.0% of statements
```
## goreportcard
```bash
➜ git-webhook-ci git:(main) ✗ make goreportcard
oreportcard-cli -v
Grade .......... A+ 100.0%
Files .................. 6
Issues ................. 0
gofmt ............... 100%
go_vet .............. 100%
gocyclo ............. 100%
ineffassign ......... 100%
license ............. 100%
misspell ............ 100%
```

3
app.example.yml Normal file
View File

@@ -0,0 +1,3 @@
script_binary_path: "/bin/bash"
webhook_script_path: "./test-script.sh"
file_to_watch_path: "./test_monitor.txt"

View File

@@ -1,45 +0,0 @@
package cfg
import (
"log"
"os/exec"
"strings"
"github.com/joho/godotenv"
"github.com/kelseyhightower/envconfig"
)
type Config struct {
ScriptBinaryPath string `required:"true" split_words:"true"`
WebhookScriptPath string `required:"true" split_words:"true"`
FileToWatchPath string `required:"true" split_words:"true"`
TestFileToWatchPath string `required:"true" split_words:"true"`
}
func rootDir() string {
cmdOut, err := exec.Command("git", "rev-parse", "--show-toplevel").Output()
if err != nil {
log.Fatalf("exec.Command: %s", err)
return ""
}
rootDir := strings.TrimSpace(string(cmdOut))
return rootDir
}
func NewConfig(isProd bool) *Config {
if !isProd {
err := godotenv.Load(rootDir() + "/.env")
if err != nil {
log.Fatalf("environment variable ENV is empty and an error occurred while loading the .env file\n")
}
}
cfg := &Config{}
err := envconfig.Process("", cfg)
if err != nil {
log.Fatalf("envconfig.Process: %s\n", err)
}
return cfg
}

9
go.mod
View File

@@ -1,17 +1,16 @@
module gitea.urkob.com/urko/git-webhook-ci module gitea.urkob.com/urko/git-webhook-ci
go 1.19 go 1.22
require ( require (
github.com/fsnotify/fsnotify v1.6.0 github.com/fsnotify/fsnotify v1.7.0
github.com/joho/godotenv v1.5.1
github.com/kelseyhightower/envconfig v1.4.0
github.com/stretchr/testify v1.8.2 github.com/stretchr/testify v1.8.2
gopkg.in/yaml.v2 v2.4.0
) )
require ( require (
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/sys v0.0.0-20220908164124-27713097b956 // indirect golang.org/x/sys v0.19.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

14
go.sum
View File

@@ -1,12 +1,8 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -16,10 +12,12 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
golang.org/x/sys v0.0.0-20220908164124-27713097b956 h1:XeJjHH1KiLpKGb6lvMiksZ9l0fVUh+AmGcm0nOMEBOY= golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

3
internal/watcher/testdata/config.yml vendored Normal file
View File

@@ -0,0 +1,3 @@
script_binary_path: "/bin/bash"
webhook_script_path: "testdata/test-script.sh"
file_to_watch_path: "testdata/test_monitor.txt"

View File

@@ -0,0 +1,2 @@
#!/bin/bash
echo "deploy script has been called"

View File

@@ -4,13 +4,23 @@ import (
"errors" "errors"
"log" "log"
pkgwatcher "gitea.urkob.com/urko/git-webhook-ci/pkg/watcher" "gitea.urkob.com/urko/git-webhook-ci/pkg"
"github.com/fsnotify/fsnotify" "github.com/fsnotify/fsnotify"
) )
type watcher struct { type watcher struct {
fswatcher *fsnotify.Watcher fswatcher *fsnotify.Watcher
deploy pkgwatcher.DeployFunc deploy pkg.DeployFunc
}
type notifier struct{}
func (n *notifier) NewWatcher() (*fsnotify.Watcher, error) {
return fsnotify.NewWatcher()
}
func NewNotifier() *notifier {
return &notifier{}
} }
var ( var (
@@ -18,8 +28,8 @@ var (
errErrorsClosedChan = errors.New("errors is closed") errErrorsClosedChan = errors.New("errors is closed")
) )
func NewWatcher(deploy pkgwatcher.DeployFunc) *watcher { func NewWatcher(notifier pkg.NotifyIface, deploy pkg.DeployFunc) *watcher {
wt, err := fsnotify.NewWatcher() wt, err := notifier.NewWatcher()
if err != nil { if err != nil {
log.Printf("fsnotify.NewWatcher: %s\n", err) log.Printf("fsnotify.NewWatcher: %s\n", err)
return nil return nil
@@ -45,10 +55,12 @@ func (w *watcher) Listen(binaryPath, scriptPath string, outputErr chan<- error)
outputErr <- errEventsClosedChan outputErr <- errEventsClosedChan
return return
} }
if !event.Has(fsnotify.Write) { if !event.Has(fsnotify.Write) {
log.Printf("is not Write: %s\n", event.Name) log.Printf("is not Write: %s\n", event.Name)
continue continue
} }
log.Printf("event: %s | op: %s \n", event.Name, event.Op)
if err := w.deploy(binaryPath, scriptPath); err != nil { if err := w.deploy(binaryPath, scriptPath); err != nil {
log.Printf("deploy: %s\n", err) log.Printf("deploy: %s\n", err)

View File

@@ -3,23 +3,37 @@ package watcher
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"testing" "testing"
"time" "time"
"gitea.urkob.com/urko/git-webhook-ci/cfg" "gitea.urkob.com/urko/git-webhook-ci/kit/config"
pkgwatcher "gitea.urkob.com/urko/git-webhook-ci/pkg/watcher" "gitea.urkob.com/urko/git-webhook-ci/pkg"
"github.com/fsnotify/fsnotify" "github.com/fsnotify/fsnotify"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
type testErrorNotifier struct {
*fsnotify.Watcher
}
func (n *testErrorNotifier) NewWatcher() (*fsnotify.Watcher, error) {
return nil, errIntentional
}
var ( var (
mockDeploy pkgwatcher.DeployFunc errNotifier = &testErrorNotifier{}
mockErrorDeploy pkgwatcher.DeployFunc okNotifier = &notifier{}
)
var (
mockDeploy pkg.DeployFunc
mockErrorDeploy pkg.DeployFunc
errIntentional = errors.New("intentional error") errIntentional = errors.New("intentional error")
binaryPath = "" binaryPath = ""
scriptPath = "" scriptPath = ""
executionMaxTimeout = time.Second * 2 executionMaxTimeout = time.Second * 2
config *cfg.Config cfg *config.Config
events = []fsnotify.Event{ events = []fsnotify.Event{
{ {
Name: "test event", Name: "test event",
@@ -32,7 +46,7 @@ var (
} }
) )
func init() { func TestMain(t *testing.M) {
mockDeploy = func(binaryPath, scriptPath string) error { mockDeploy = func(binaryPath, scriptPath string) error {
return nil return nil
} }
@@ -40,8 +54,16 @@ func init() {
mockErrorDeploy = func(binaryPath, scriptPath string) error { mockErrorDeploy = func(binaryPath, scriptPath string) error {
return errIntentional return errIntentional
} }
}
func LoadConfig(t *testing.T) {
t.Helper()
cf, err := config.LoadConfig("testdata/config.yaml")
if err != nil {
panic(fmt.Errorf("Error loading config: %w", err))
}
cfg = cf
config = cfg.NewConfig(false)
} }
func sendTestEvents(w *watcher) { func sendTestEvents(w *watcher) {
@@ -50,33 +72,47 @@ func sendTestEvents(w *watcher) {
} }
} }
func getNewWatcher() *watcher { func newWatcher() *watcher {
return NewWatcher(mockDeploy) return NewWatcher(okNotifier, mockDeploy)
} }
func getNewWatcherWithError() *watcher { func newWatcherWithDeployError() *watcher {
return NewWatcher(mockErrorDeploy) return NewWatcher(okNotifier, mockErrorDeploy)
}
func newWatcherWithCtorError() *watcher {
return NewWatcher(errNotifier, mockDeploy)
}
func Test_NewNotifier(t *testing.T) {
LoadConfig(t)
require.NotNil(t, NewNotifier())
} }
func Test_NewWatcher(t *testing.T) { func Test_NewWatcher(t *testing.T) {
w := getNewWatcher() w := newWatcher()
require.NotNil(t, w) require.NotNil(t, w)
} }
func Test_ErrorNewWatcher(t *testing.T) {
w := newWatcherWithCtorError()
require.Nil(t, w)
}
func Test_Close(t *testing.T) { func Test_Close(t *testing.T) {
w := getNewWatcher() w := newWatcher()
err := w.Close() err := w.Close()
require.NoError(t, err) require.NoError(t, err)
} }
func Test_Monitor(t *testing.T) { func Test_Monitor(t *testing.T) {
w := getNewWatcher() w := newWatcher()
err := w.Monitor(config.TestFileToWatchPath) err := w.Monitor(cfg.FileToWatchPath)
require.NoError(t, err) require.NoError(t, err)
} }
func Test_ListenSuccess(t *testing.T) { func Test_ListenSuccess(t *testing.T) {
w := getNewWatcher() w := newWatcher()
ctx, errors := listenWithSendEvents(w) ctx, errors := listenWithSendEvents(w)
for { for {
@@ -91,7 +127,7 @@ func Test_ListenSuccess(t *testing.T) {
} }
func Test_ListenError(t *testing.T) { func Test_ListenError(t *testing.T) {
w := getNewWatcherWithError() w := newWatcherWithDeployError()
ctx, errors := listenWithSendEvents(w) ctx, errors := listenWithSendEvents(w)
for { for {
@@ -107,7 +143,7 @@ func Test_ListenError(t *testing.T) {
} }
func Test_ListenErrorChanClose(t *testing.T) { func Test_ListenErrorChanClose(t *testing.T) {
w := getNewWatcher() w := newWatcher()
ctx, errors := listenWithSendEvents(w) ctx, errors := listenWithSendEvents(w)
close(w.fswatcher.Events) close(w.fswatcher.Events)
for { for {

27
kit/config/config.go Normal file
View File

@@ -0,0 +1,27 @@
package config
import (
"os"
"gopkg.in/yaml.v2"
)
type Config struct {
ScriptBinaryPath string `yaml:"script_binary_path"`
WebhookScriptPath string `yaml:"webhook_script_path"`
FileToWatchPath string `yaml:"file_to_watch_path"`
}
func LoadConfig(path string) (*Config, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, err
}
var config Config
if err := yaml.Unmarshal(data, &config); err != nil {
return nil, err
}
return &config, nil
}

42
main.go
View File

@@ -6,18 +6,24 @@ import (
"os/signal" "os/signal"
"syscall" "syscall"
"gitea.urkob.com/urko/git-webhook-ci/cfg"
"gitea.urkob.com/urko/git-webhook-ci/internal/watcher" "gitea.urkob.com/urko/git-webhook-ci/internal/watcher"
pkgwatcher "gitea.urkob.com/urko/git-webhook-ci/pkg/watcher" "gitea.urkob.com/urko/git-webhook-ci/kit/config"
"gitea.urkob.com/urko/git-webhook-ci/pkg"
) )
var watcherIface pkgwatcher.WatcherIface var (
watcherIface pkg.WatcherIface
notifierIface pkg.NotifyIface
)
func main() { func main() {
isProd := os.Getenv("ENV") == "prod" config, err := config.LoadConfig("./configs/app.yaml")
config := cfg.NewConfig(isProd) if err != nil {
panic(err)
}
watcherIface = watcher.NewWatcher(pkgwatcher.Deploy) notifierIface = watcher.NewNotifier()
watcherIface = watcher.NewWatcher(notifierIface, pkg.Deploy)
defer func() { defer func() {
if err := watcherIface.Close(); err != nil { if err := watcherIface.Close(); err != nil {
@@ -35,21 +41,15 @@ func main() {
// Handle termination on ctrl+signalChan // Handle termination on ctrl+signalChan
signalChan := make(chan os.Signal, 1) signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM) signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM)
for {
go func(errors chan error) { select {
for { case <-signalChan:
select { os.Exit(1)
case <-signalChan: case err := <-errors:
os.Exit(1) if err != nil {
case err := <-errors: log.Printf("watcherIface.Monitor: %s\n", err)
if err != nil { continue
log.Fatalf("watcherIface.Monitor: %s\n", err)
return
}
} }
} }
}(errors) }
// TODO: Improve this: Block main goroutine forever.
<-make(chan struct{})
} }

2
pkg/testdata/test-script.sh vendored Normal file
View File

@@ -0,0 +1,2 @@
#!/bin/bash
echo "deploy script has been called"

View File

@@ -1,12 +1,18 @@
package watcher package pkg
import ( import (
"fmt" "fmt"
"log" "log"
"os" "os"
"os/exec" "os/exec"
"github.com/fsnotify/fsnotify"
) )
type NotifyIface interface {
NewWatcher() (*fsnotify.Watcher, error)
}
type WatcherIface interface { type WatcherIface interface {
Monitor(path string) error Monitor(path string) error
Listen(binaryPath, scriptPath string, outputErr chan<- error) Listen(binaryPath, scriptPath string, outputErr chan<- error)

32
pkg/watcher_test.go Normal file
View File

@@ -0,0 +1,32 @@
package pkg
import (
"testing"
"github.com/stretchr/testify/require"
)
const (
binaryPath = "/bin/bash"
scriptPath = "testdata/test-script.sh"
)
func TestDeploy(t *testing.T) {
err := Deploy(binaryPath, scriptPath)
require.NoError(t, err)
}
func TestDeployError(t *testing.T) {
err := Deploy("", "")
require.Error(t, err)
}
func TestExecute(t *testing.T) {
err := execute(binaryPath, scriptPath)
require.NoError(t, err)
}
func TestExecuteError(t *testing.T) {
err := execute("", "")
require.Error(t, err)
}