From eb36349ce51fdcdc7910a4bedc00420b11535b45 Mon Sep 17 00:00:00 2001 From: MrMelon54 Date: Wed, 8 Jan 2025 22:31:57 +0000 Subject: [PATCH] Add post upload hook --- cmd/bluebell/main.go | 14 +++++++++++--- go.mod | 1 + go.sum | 2 ++ hook/hook.go | 33 +++++++++++++++++++++++++++++++++ upload/upload.go | 12 ++++++++++-- upload/upload_test.go | 6 +++--- 6 files changed, 60 insertions(+), 8 deletions(-) create mode 100644 hook/hook.go diff --git a/cmd/bluebell/main.go b/cmd/bluebell/main.go index 66242f2..0c9fc59 100644 --- a/cmd/bluebell/main.go +++ b/cmd/bluebell/main.go @@ -7,6 +7,7 @@ import ( "github.com/1f349/bluebell" "github.com/1f349/bluebell/api" "github.com/1f349/bluebell/conf" + "github.com/1f349/bluebell/hook" "github.com/1f349/bluebell/logger" "github.com/1f349/bluebell/serve" "github.com/1f349/bluebell/upload" @@ -69,15 +70,21 @@ func main() { wd := filepath.Dir(*configPath) sitesDir := filepath.Join(wd, "sites") + sitesPostHookDir := filepath.Join(wd, "hooks/post") keyStore, err := mjwt.NewKeyStoreFromPath(filepath.Join(wd, "keystore")) if err != nil { logger.Logger.Fatal("Failed to load MJWT keystore", "dir", filepath.Join(wd, "keystore"), "err", err) } - _, err = os.Stat(sitesDir) + err = os.MkdirAll(sitesDir, 0770) if err != nil { - logger.Logger.Fatal("Failed to find sites, does the directory exist? Error: ", err) + logger.Logger.Fatal("Failed to find or create sites directory. Error: ", err) + } + + err = os.MkdirAll(sitesPostHookDir, 0770) + if err != nil { + logger.Logger.Fatal("Failed to find or create sites directory. Error: ", err) } sitesFs := afero.NewBasePathFs(afero.NewOsFs(), sitesDir) @@ -112,7 +119,8 @@ func main() { } serveHandler := serve.New(sitesFs, db) - uploadHandler := upload.New(sitesFs, db) + postHook := hook.New(sitesPostHookDir, sitesDir) + uploadHandler := upload.New(sitesFs, db, postHook) apiHandler := api.New(uploadHandler, keyStore, db) serverHttp := &http.Server{ diff --git a/go.mod b/go.mod index 0d5230f..64d97e4 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/1f349/syncmap v0.0.3 github.com/charmbracelet/log v0.4.0 github.com/cloudflare/tableflip v1.2.3 + github.com/cyphar/filepath-securejoin v0.3.6 github.com/dustin/go-humanize v1.0.1 github.com/golang-migrate/migrate/v4 v4.18.1 github.com/google/uuid v1.6.0 diff --git a/go.sum b/go.sum index 7330e28..4f2ec92 100644 --- a/go.sum +++ b/go.sum @@ -16,6 +16,8 @@ github.com/charmbracelet/x/ansi v0.6.0 h1:qOznutrb93gx9oMiGf7caF7bqqubh6YIM0SWKy github.com/charmbracelet/x/ansi v0.6.0/go.mod h1:KBUFw1la39nl0dLl10l5ORDAqGXaeurTQmwyyVKse/Q= github.com/cloudflare/tableflip v1.2.3 h1:8I+B99QnnEWPHOY3fWipwVKxS70LGgUsslG7CSfmHMw= github.com/cloudflare/tableflip v1.2.3/go.mod h1:P4gRehmV6Z2bY5ao5ml9Pd8u6kuEnlB37pUFMmv7j2E= +github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM= +github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= 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/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= diff --git a/hook/hook.go b/hook/hook.go new file mode 100644 index 0000000..5b2c4ea --- /dev/null +++ b/hook/hook.go @@ -0,0 +1,33 @@ +package hook + +import ( + "github.com/cyphar/filepath-securejoin" + "os/exec" + "path/filepath" +) + +type Hook struct { + hookDir string + sitesDir string +} + +func New(hookDir string, sitesDir string) *Hook { + return &Hook{hookDir: hookDir, sitesDir: sitesDir} +} + +func (h *Hook) Run(site, branch string) error { + sitePath, err := securejoin.SecureJoin(h.sitesDir, site+"/@"+branch) + if err != nil { + return err + } + scriptPath, err := securejoin.SecureJoin(h.hookDir, site) + if err != nil { + return err + } + cmd := exec.Cmd{ + Path: scriptPath, + Args: []string{filepath.Base(scriptPath)}, + Dir: sitePath, + } + return cmd.Run() +} diff --git a/upload/upload.go b/upload/upload.go index 3ed0110..0417cd2 100644 --- a/upload/upload.go +++ b/upload/upload.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "github.com/1f349/bluebell/database" + "github.com/1f349/bluebell/hook" "github.com/1f349/bluebell/validation" "github.com/1f349/syncmap" "github.com/dustin/go-humanize" @@ -34,8 +35,8 @@ type uploadQueries interface { UpdateBranch(ctx context.Context, arg database.UpdateBranchParams) error } -func New(storage afero.Fs, db uploadQueries) *Handler { - return &Handler{storageFs: storage, db: db} +func New(storage afero.Fs, db uploadQueries, hook *hook.Hook) *Handler { + return &Handler{storageFs: storage, db: db, postHook: hook} } const maxFileSize = 1 * humanize.GiByte @@ -44,6 +45,7 @@ type Handler struct { storageFs afero.Fs db uploadQueries mu syncmap.Map[string, *sync.Mutex] + postHook *hook.Hook } func (h *Handler) Handle(rw http.ResponseWriter, req *http.Request, params httprouter.Params) { @@ -165,6 +167,12 @@ func (h *Handler) extractTarGzUpload(fileData io.Reader, site, branch string) er } } + // call the post hook script + err = h.postHook.Run(site, branch) + if err != nil { + return err + } + // TODO(melon): I would love to use unix.Renameat2 but due to afero this will not work err = h.storageFs.Rename(siteBranchPath, siteBranchOldPath) diff --git a/upload/upload_test.go b/upload/upload_test.go index 04f4173..7edd0de 100644 --- a/upload/upload_test.go +++ b/upload/upload_test.go @@ -117,7 +117,7 @@ func (f *fakeUploadDB) UpdateBranch(ctx context.Context, arg database.UpdateBran func TestHandler_Handle(t *testing.T) { fs := afero.NewMemMapFs() - h := New(fs, new(fakeUploadDB)) + h := New(fs, new(fakeUploadDB), nil) r := httprouter.New() r.POST("/u/:site/:branch", h.Handle) @@ -168,7 +168,7 @@ func extractTarGzUploadTest(t *testing.T, db uploadQueries) { for _, branch := range []string{"main", "test", "dev"} { t.Run(branch+" branch", func(t *testing.T) { fs := afero.NewMemMapFs() - h := New(fs, db) + h := New(fs, db, nil) buffer := bytes.NewBuffer(testArchiveTarGz) assert.NoError(t, h.extractTarGzUpload(buffer, "example.com", branch)) @@ -188,7 +188,7 @@ func TestHandler_extractTarGzUpload_memoryDB_multiple(t *testing.T) { func extractTarGzUploadMultipleTest(t *testing.T, db uploadQueries) { fs := afero.NewMemMapFs() - h := New(fs, db) + h := New(fs, db, nil) sig := new(atomic.Bool) wg := new(sync.WaitGroup)