diff options
| author | 2026-03-07 21:21:58 +0800 | |
|---|---|---|
| committer | 2026-03-07 21:31:26 +0800 | |
| commit | 5b8941986d4c3f398fc1fc2d1314e80510be346c (patch) | |
| tree | 57cddb68974485b027a5345b4b91e341975aecd6 /receivepack | |
| parent | receivepack: Re-organize things around (diff) | |
| signature | No signature | |
receivepack: Fix lint
Diffstat (limited to 'receivepack')
| -rw-r--r-- | receivepack/int_test.go | 30 | ||||
| -rw-r--r-- | receivepack/internal/service/apply.go | 1 | ||||
| -rw-r--r-- | receivepack/internal/service/execute.go | 140 | ||||
| -rw-r--r-- | receivepack/internal/service/ingest_quarantine.go | 75 | ||||
| -rw-r--r-- | receivepack/internal/service/quarantine.go | 7 | ||||
| -rw-r--r-- | receivepack/internal/service/quarantine_test.go | 225 | ||||
| -rw-r--r-- | receivepack/internal/service/run_hook.go | 73 |
7 files changed, 289 insertions, 262 deletions
diff --git a/receivepack/int_test.go b/receivepack/int_test.go index e05a9bda..c2d260bb 100644 --- a/receivepack/int_test.go +++ b/receivepack/int_test.go @@ -52,7 +52,8 @@ func TestReceivePackDeleteOnlyAtomicDeleteSucceeds(t *testing.T) { t.Fatalf("unexpected receive-pack output %q", got) } - if _, err := repo.Refs().Resolve("refs/heads/main"); err == nil { + _, err = repo.Refs().Resolve("refs/heads/main") + if err == nil { t.Fatal("refs/heads/main still exists after delete push") } }) @@ -101,11 +102,13 @@ func TestReceivePackDeleteOnlyNonAtomicAppliesIndependentDeletes(t *testing.T) { t.Fatalf("unexpected receive-pack output %q", got) } - if _, err := repo.Refs().Resolve("refs/heads/main"); err != nil { + _, err = repo.Refs().Resolve("refs/heads/main") + if err != nil { t.Fatalf("Resolve(main): %v", err) } - if _, err := repo.Refs().Resolve("refs/heads/topic"); err == nil { + _, err = repo.Refs().Resolve("refs/heads/topic") + if err == nil { t.Fatal("refs/heads/topic still exists after successful delete") } }) @@ -154,11 +157,13 @@ func TestReceivePackDeleteOnlyAtomicFailureLeavesAllRefsUntouched(t *testing.T) t.Fatalf("unexpected receive-pack output %q", got) } - if _, err := repo.Refs().Resolve("refs/heads/main"); err != nil { + _, err = repo.Refs().Resolve("refs/heads/main") + if err != nil { t.Fatalf("Resolve(main): %v", err) } - if _, err := repo.Refs().Resolve("refs/heads/topic"); err != nil { + _, err = repo.Refs().Resolve("refs/heads/topic") + if err != nil { t.Fatalf("Resolve(topic): %v", err) } }) @@ -449,11 +454,13 @@ func TestReceivePackHookSeesQuarantinedObjectsAndCanRejectBeforePromotion(t *tes t.Fatalf("unexpected hook updates: %+v", req.Updates) } - if _, _, err := req.ExistingObjects.ReadHeader(commitID); err == nil { + _, _, err := req.ExistingObjects.ReadHeader(commitID) + if err == nil { t.Fatalf("existing objects unexpectedly contained quarantined commit %s", commitID) } - if _, _, err := req.QuarantinedObjects.ReadHeader(commitID); err != nil { + _, _, err = req.QuarantinedObjects.ReadHeader(commitID) + if err != nil { t.Fatalf("quarantined objects missing commit %s: %v", commitID, err) } @@ -477,7 +484,8 @@ func TestReceivePackHookSeesQuarantinedObjectsAndCanRejectBeforePromotion(t *tes t.Fatalf("unexpected receive-pack output %q", got) } - if _, err := repo.Refs().Resolve("refs/heads/main"); err == nil { + _, err = repo.Refs().Resolve("refs/heads/main") + if err == nil { t.Fatal("refs/heads/main exists after hook rejection") } @@ -535,11 +543,13 @@ func TestReceivePackHookCanRejectSubsetOfNonAtomicDeleteOnlyPush(t *testing.T) { t.Fatalf("unexpected receive-pack output %q", got) } - if _, err := repo.Refs().Resolve("refs/heads/main"); err != nil { + _, err = repo.Refs().Resolve("refs/heads/main") + if err != nil { t.Fatalf("Resolve(main): %v", err) } - if _, err := repo.Refs().Resolve("refs/heads/topic"); err == nil { + _, err = repo.Refs().Resolve("refs/heads/topic") + if err == nil { t.Fatal("refs/heads/topic still exists after successful delete") } }) diff --git a/receivepack/internal/service/apply.go b/receivepack/internal/service/apply.go index c24d2a95..f802e0e8 100644 --- a/receivepack/internal/service/apply.go +++ b/receivepack/internal/service/apply.go @@ -15,6 +15,7 @@ func (service *Service) applyAtomic(result *Result, commands []Command) error { err = queueWriteTransaction(tx, command) if err != nil { _ = tx.Abort() + fillCommandErrors(result, commands, err.Error()) return nil diff --git a/receivepack/internal/service/execute.go b/receivepack/internal/service/execute.go index 8febfc79..14468799 100644 --- a/receivepack/internal/service/execute.go +++ b/receivepack/internal/service/execute.go @@ -3,8 +3,6 @@ package service import ( "context" "os" - - "codeberg.org/lindenii/furgit/format/pack/ingest" ) // Execute validates one receive-pack request, optionally ingests its pack into @@ -13,68 +11,23 @@ func (service *Service) Execute(ctx context.Context, req *Request) (*Result, err result := &Result{ Commands: make([]CommandResult, 0, len(req.Commands)), } + var ( quarantineName string quarantineRoot *os.Root err error ) - if req.PackExpected { - if req.Pack == nil { - result.UnpackError = "missing pack stream" - fillCommandErrors(result, req.Commands, "missing pack stream") - - return result, nil - } - - if service.opts.ObjectsRoot == nil { - result.UnpackError = "objects root not configured" - fillCommandErrors(result, req.Commands, "objects root not configured") - - return result, nil - } - - quarantineName, quarantineRoot, err = service.createQuarantineRoot() - if err != nil { - result.UnpackError = err.Error() - fillCommandErrors(result, req.Commands, err.Error()) - - return result, nil - } + quarantineName, quarantineRoot, ok := service.ingestQuarantine(result, req.Commands, req) + if !ok { + return result, nil + } + if quarantineRoot != nil { defer func() { _ = quarantineRoot.Close() _ = service.opts.ObjectsRoot.RemoveAll(quarantineName) }() - - quarantinePackRoot, err := service.openQuarantinePackRoot(quarantineRoot) - if err != nil { - result.UnpackError = err.Error() - fillCommandErrors(result, req.Commands, err.Error()) - - return result, nil - } - - defer func() { - _ = quarantinePackRoot.Close() - }() - - ingested, err := ingest.Ingest( - req.Pack, - quarantinePackRoot, - service.opts.Algorithm, - true, - true, - service.opts.ExistingObjects, - ) - if err != nil { - result.UnpackError = err.Error() - fillCommandErrors(result, req.Commands, err.Error()) - - return result, nil - } - - result.Ingest = &ingested } for _, command := range req.Commands { @@ -90,75 +43,30 @@ func (service *Service) Execute(ctx context.Context, req *Request) (*Result, err return result, nil } - allowedCommands := append([]Command(nil), req.Commands...) - allowedIndices := make([]int, 0, len(req.Commands)) - for index := range req.Commands { - allowedIndices = append(allowedIndices, index) - } - rejected := make(map[int]string) - - if service.opts.Hook != nil { - quarantinedObjects, err := service.openQuarantinedObjects(quarantineName) - if err != nil { - fillCommandErrors(result, req.Commands, err.Error()) - - return result, nil - } - - defer func() { - _ = quarantinedObjects.Close() - }() - - decisions, err := service.opts.Hook(ctx, HookRequest{ - Refs: service.opts.Refs, - ExistingObjects: service.opts.ExistingObjects, - QuarantinedObjects: quarantinedObjects, - Updates: buildHookUpdates(req.Commands), - PushOptions: append([]string(nil), req.PushOptions...), - }) - if err != nil { - fillCommandErrors(result, req.Commands, err.Error()) - - return result, nil - } - - if len(decisions) != len(req.Commands) { - fillCommandErrors(result, req.Commands, "hook returned wrong number of update decisions") - - return result, nil - } - - allowedCommands = allowedCommands[:0] - allowedIndices = allowedIndices[:0] - for index, decision := range decisions { - if decision.Accept { - allowedCommands = append(allowedCommands, req.Commands[index]) - allowedIndices = append(allowedIndices, index) + allowedCommands, allowedIndices, rejected, ok, errText := service.runHook( + ctx, + req, + req.Commands, + quarantineName, + ) + if !ok { + fillCommandErrors(result, req.Commands, errText) - continue - } + return result, nil + } - message := decision.Message + if req.Atomic && len(rejected) != 0 { + result.Commands = make([]CommandResult, 0, len(req.Commands)) + for index, command := range req.Commands { + message := rejected[index] if message == "" { - message = "rejected by hook" + message = "atomic push rejected by hook" } - rejected[index] = message + result.Commands = append(result.Commands, resultForHookRejection(command, message)) } - if req.Atomic && len(rejected) != 0 { - result.Commands = make([]CommandResult, 0, len(req.Commands)) - for index, command := range req.Commands { - message := rejected[index] - if message == "" { - message = "atomic push rejected by hook" - } - - result.Commands = append(result.Commands, resultForHookRejection(command, message)) - } - - return result, nil - } + return result, nil } if len(allowedCommands) == 0 { @@ -181,6 +89,7 @@ func (service *Service) Execute(ctx context.Context, req *Request) (*Result, err if req.Atomic { subresult := &Result{} + err := service.applyAtomic(subresult, allowedCommands) if err != nil { return result, err @@ -193,6 +102,7 @@ func (service *Service) Execute(ctx context.Context, req *Request) (*Result, err } subresult := &Result{} + err = service.applyBatch(subresult, allowedCommands) if err != nil { return result, err diff --git a/receivepack/internal/service/ingest_quarantine.go b/receivepack/internal/service/ingest_quarantine.go new file mode 100644 index 00000000..bf918c6d --- /dev/null +++ b/receivepack/internal/service/ingest_quarantine.go @@ -0,0 +1,75 @@ +package service + +import ( + "os" + + "codeberg.org/lindenii/furgit/format/pack/ingest" +) + +func (service *Service) ingestQuarantine( + result *Result, + commands []Command, + req *Request, +) (string, *os.Root, bool) { + if !req.PackExpected { + return "", nil, true + } + + if req.Pack == nil { + result.UnpackError = "missing pack stream" + fillCommandErrors(result, commands, "missing pack stream") + + return "", nil, false + } + + if service.opts.ObjectsRoot == nil { + result.UnpackError = "objects root not configured" + fillCommandErrors(result, commands, "objects root not configured") + + return "", nil, false + } + + quarantineName, quarantineRoot, err := service.createQuarantineRoot() + if err != nil { + result.UnpackError = err.Error() + fillCommandErrors(result, commands, err.Error()) + + return "", nil, false + } + + quarantinePackRoot, err := service.openQuarantinePackRoot(quarantineRoot) + if err != nil { + result.UnpackError = err.Error() + fillCommandErrors(result, commands, err.Error()) + + _ = quarantineRoot.Close() + _ = service.opts.ObjectsRoot.RemoveAll(quarantineName) + + return "", nil, false + } + + ingested, err := ingest.Ingest( + req.Pack, + quarantinePackRoot, + service.opts.Algorithm, + true, + true, + service.opts.ExistingObjects, + ) + + _ = quarantinePackRoot.Close() + + if err != nil { + result.UnpackError = err.Error() + fillCommandErrors(result, commands, err.Error()) + + _ = quarantineRoot.Close() + _ = service.opts.ObjectsRoot.RemoveAll(quarantineName) + + return "", nil, false + } + + result.Ingest = &ingested + + return quarantineName, quarantineRoot, true +} diff --git a/receivepack/internal/service/quarantine.go b/receivepack/internal/service/quarantine.go index f263186b..97a85959 100644 --- a/receivepack/internal/service/quarantine.go +++ b/receivepack/internal/service/quarantine.go @@ -169,16 +169,17 @@ func finalizeQuarantineFile( return applyPromotedFilePermissions(root, dst, perms) case !errors.Is(err, fs.ErrExist): _, statErr := root.Stat(dst) - if statErr == nil { + switch { + case statErr == nil: err = fs.ErrExist - } else if errors.Is(statErr, fs.ErrNotExist) { + case errors.Is(statErr, fs.ErrNotExist): renameErr := root.Rename(src, dst) if renameErr == nil { return applyPromotedFilePermissions(root, dst, perms) } err = renameErr - } else { + default: _ = root.Remove(src) return statErr diff --git a/receivepack/internal/service/quarantine_test.go b/receivepack/internal/service/quarantine_test.go index b28868ed..0bab3728 100644 --- a/receivepack/internal/service/quarantine_test.go +++ b/receivepack/internal/service/quarantine_test.go @@ -1,4 +1,6 @@ -package service +package service //nolint:testpackage + +// because we need access to quarantine internals import ( "os" @@ -9,167 +11,161 @@ import ( "codeberg.org/lindenii/furgit/objectstore/memory" ) -func TestPromoteQuarantineAppliesConfiguredPermissions(t *testing.T) { - t.Parallel() +type quarantineFixture struct { + svc *Service + objectsRoot *os.Root + quarantineName string + quarantineRoot *os.Root +} - objectsDir := t.TempDir() - objectsRoot, err := os.OpenRoot(objectsDir) +func newQuarantineFixture(tb testing.TB, opts Options) *quarantineFixture { + tb.Helper() + + objectsRoot, err := os.OpenRoot(tb.TempDir()) if err != nil { - t.Fatalf("os.OpenRoot: %v", err) + tb.Fatalf("os.OpenRoot: %v", err) } - t.Cleanup(func() { + tb.Cleanup(func() { _ = objectsRoot.Close() }) - svc := New(Options{ - Algorithm: objectid.AlgorithmSHA1, - ExistingObjects: memory.New(objectid.AlgorithmSHA1), - ObjectsRoot: objectsRoot, - PromotedObjectPermissions: &PromotedObjectPermissions{ - DirMode: 0o751, - FileMode: 0o640, - }, - }) + opts.Algorithm = objectid.AlgorithmSHA1 + opts.ExistingObjects = memory.New(objectid.AlgorithmSHA1) + opts.ObjectsRoot = objectsRoot + + svc := New(opts) quarantineName, quarantineRoot, err := svc.createQuarantineRoot() if err != nil { - t.Fatalf("createQuarantineRoot: %v", err) + tb.Fatalf("createQuarantineRoot: %v", err) } - t.Cleanup(func() { + tb.Cleanup(func() { _ = quarantineRoot.Close() _ = objectsRoot.RemoveAll(quarantineName) }) - if err := quarantineRoot.Mkdir("ab", 0o700); err != nil { - t.Fatalf("Mkdir(ab): %v", err) + return &quarantineFixture{ + svc: svc, + objectsRoot: objectsRoot, + quarantineName: quarantineName, + quarantineRoot: quarantineRoot, } +} - if err := quarantineRoot.WriteFile(path.Join("ab", "cdef"), []byte("payload"), 0o600); err != nil { - t.Fatalf("WriteFile(quarantine loose): %v", err) - } +func writeMatchingPromotedFile( + tb testing.TB, + quarantineRoot, objectsRoot *os.Root, + dir, name, payload string, +) { + tb.Helper() - if err := svc.promoteQuarantine(quarantineName, quarantineRoot); err != nil { - t.Fatalf("promoteQuarantine: %v", err) + err := quarantineRoot.Mkdir(dir, 0o755) + if err != nil { + tb.Fatalf("Mkdir(%s): %v", dir, err) } - dirInfo, err := objectsRoot.Stat("ab") + err = objectsRoot.Mkdir(dir, 0o755) if err != nil { - t.Fatalf("Stat(ab): %v", err) + tb.Fatalf("Mkdir(dst %s): %v", dir, err) } - if got := dirInfo.Mode().Perm(); got != 0o751 { - t.Fatalf("dir mode = %o, want 751", got) - } + rel := path.Join(dir, name) - fileInfo, err := objectsRoot.Stat(path.Join("ab", "cdef")) + err = quarantineRoot.WriteFile(rel, []byte(payload), 0o644) if err != nil { - t.Fatalf("Stat(ab/cdef): %v", err) + tb.Fatalf("WriteFile(quarantine %s): %v", rel, err) } - if got := fileInfo.Mode().Perm(); got != 0o640 { - t.Fatalf("file mode = %o, want 640", got) + err = objectsRoot.WriteFile(rel, []byte(payload), 0o644) + if err != nil { + tb.Fatalf("WriteFile(permanent %s): %v", rel, err) } } -func TestPromoteQuarantineTreatsExistingLooseObjectAsSuccess(t *testing.T) { +func TestPromoteQuarantineAppliesConfiguredPermissions(t *testing.T) { t.Parallel() - objectsDir := t.TempDir() - objectsRoot, err := os.OpenRoot(objectsDir) - if err != nil { - t.Fatalf("os.OpenRoot: %v", err) - } - - t.Cleanup(func() { - _ = objectsRoot.Close() - }) - - svc := New(Options{ - Algorithm: objectid.AlgorithmSHA1, - ExistingObjects: memory.New(objectid.AlgorithmSHA1), - ObjectsRoot: objectsRoot, + fx := newQuarantineFixture(t, Options{ + PromotedObjectPermissions: &PromotedObjectPermissions{ + DirMode: 0o751, + FileMode: 0o640, + }, }) - quarantineName, quarantineRoot, err := svc.createQuarantineRoot() + err := fx.quarantineRoot.Mkdir("ab", 0o700) if err != nil { - t.Fatalf("createQuarantineRoot: %v", err) + t.Fatalf("Mkdir(ab): %v", err) } - t.Cleanup(func() { - _ = quarantineRoot.Close() - _ = objectsRoot.RemoveAll(quarantineName) - }) + err = fx.quarantineRoot.WriteFile(path.Join("ab", "cdef"), []byte("payload"), 0o600) + if err != nil { + t.Fatalf("WriteFile(quarantine loose): %v", err) + } - if err := quarantineRoot.Mkdir("ab", 0o755); err != nil { - t.Fatalf("Mkdir(ab): %v", err) + err = fx.svc.promoteQuarantine(fx.quarantineName, fx.quarantineRoot) + if err != nil { + t.Fatalf("promoteQuarantine: %v", err) } - if err := objectsRoot.Mkdir("ab", 0o755); err != nil { - t.Fatalf("Mkdir(dst ab): %v", err) + dirInfo, err := fx.objectsRoot.Stat("ab") + if err != nil { + t.Fatalf("Stat(ab): %v", err) } - const payload = "same object bytes" - if err := quarantineRoot.WriteFile(path.Join("ab", "cdef"), []byte(payload), 0o644); err != nil { - t.Fatalf("WriteFile(quarantine loose): %v", err) + if got := dirInfo.Mode().Perm(); got != 0o751 { + t.Fatalf("dir mode = %o, want 751", got) } - if err := objectsRoot.WriteFile(path.Join("ab", "cdef"), []byte(payload), 0o644); err != nil { - t.Fatalf("WriteFile(permanent loose): %v", err) + fileInfo, err := fx.objectsRoot.Stat(path.Join("ab", "cdef")) + if err != nil { + t.Fatalf("Stat(ab/cdef): %v", err) } - if err := svc.promoteQuarantine(quarantineName, quarantineRoot); err != nil { - t.Fatalf("promoteQuarantine: %v", err) + if got := fileInfo.Mode().Perm(); got != 0o640 { + t.Fatalf("file mode = %o, want 640", got) } } -func TestPromoteQuarantineRejectsDifferentExistingPackFile(t *testing.T) { +func TestPromoteQuarantineTreatsExistingLooseObjectAsSuccess(t *testing.T) { t.Parallel() - objectsDir := t.TempDir() - objectsRoot, err := os.OpenRoot(objectsDir) + fx := newQuarantineFixture(t, Options{}) + writeMatchingPromotedFile(t, fx.quarantineRoot, fx.objectsRoot, "ab", "cdef", "same object bytes") + + err := fx.svc.promoteQuarantine(fx.quarantineName, fx.quarantineRoot) if err != nil { - t.Fatalf("os.OpenRoot: %v", err) + t.Fatalf("promoteQuarantine: %v", err) } +} - t.Cleanup(func() { - _ = objectsRoot.Close() - }) +func TestPromoteQuarantineRejectsDifferentExistingPackFile(t *testing.T) { + t.Parallel() - svc := New(Options{ - Algorithm: objectid.AlgorithmSHA1, - ExistingObjects: memory.New(objectid.AlgorithmSHA1), - ObjectsRoot: objectsRoot, - }) + fx := newQuarantineFixture(t, Options{}) - quarantineName, quarantineRoot, err := svc.createQuarantineRoot() + err := fx.quarantineRoot.Mkdir("pack", 0o755) if err != nil { - t.Fatalf("createQuarantineRoot: %v", err) - } - - t.Cleanup(func() { - _ = quarantineRoot.Close() - _ = objectsRoot.RemoveAll(quarantineName) - }) - - if err := quarantineRoot.Mkdir("pack", 0o755); err != nil { t.Fatalf("Mkdir(pack): %v", err) } - if err := objectsRoot.Mkdir("pack", 0o755); err != nil { + err = fx.objectsRoot.Mkdir("pack", 0o755) + if err != nil { t.Fatalf("Mkdir(dst pack): %v", err) } - if err := quarantineRoot.WriteFile(path.Join("pack", "pack-a.pack"), []byte("new bytes"), 0o644); err != nil { + err = fx.quarantineRoot.WriteFile(path.Join("pack", "pack-a.pack"), []byte("new bytes"), 0o644) + if err != nil { t.Fatalf("WriteFile(quarantine pack): %v", err) } - if err := objectsRoot.WriteFile(path.Join("pack", "pack-a.pack"), []byte("old bytes"), 0o644); err != nil { + err = fx.objectsRoot.WriteFile(path.Join("pack", "pack-a.pack"), []byte("old bytes"), 0o644) + if err != nil { t.Fatalf("WriteFile(permanent pack): %v", err) } - err = svc.promoteQuarantine(quarantineName, quarantineRoot) + err = fx.svc.promoteQuarantine(fx.quarantineName, fx.quarantineRoot) if err == nil { t.Fatal("promoteQuarantine unexpectedly succeeded") } @@ -178,50 +174,11 @@ func TestPromoteQuarantineRejectsDifferentExistingPackFile(t *testing.T) { func TestPromoteQuarantineAcceptsMatchingExistingPackFile(t *testing.T) { t.Parallel() - objectsDir := t.TempDir() - objectsRoot, err := os.OpenRoot(objectsDir) - if err != nil { - t.Fatalf("os.OpenRoot: %v", err) - } - - t.Cleanup(func() { - _ = objectsRoot.Close() - }) - - svc := New(Options{ - Algorithm: objectid.AlgorithmSHA1, - ExistingObjects: memory.New(objectid.AlgorithmSHA1), - ObjectsRoot: objectsRoot, - }) + fx := newQuarantineFixture(t, Options{}) + writeMatchingPromotedFile(t, fx.quarantineRoot, fx.objectsRoot, "pack", "pack-a.pack", "identical pack bytes") - quarantineName, quarantineRoot, err := svc.createQuarantineRoot() + err := fx.svc.promoteQuarantine(fx.quarantineName, fx.quarantineRoot) if err != nil { - t.Fatalf("createQuarantineRoot: %v", err) - } - - t.Cleanup(func() { - _ = quarantineRoot.Close() - _ = objectsRoot.RemoveAll(quarantineName) - }) - - if err := quarantineRoot.Mkdir("pack", 0o755); err != nil { - t.Fatalf("Mkdir(pack): %v", err) - } - - if err := objectsRoot.Mkdir("pack", 0o755); err != nil { - t.Fatalf("Mkdir(dst pack): %v", err) - } - - const payload = "identical pack bytes" - if err := quarantineRoot.WriteFile(path.Join("pack", "pack-a.pack"), []byte(payload), 0o644); err != nil { - t.Fatalf("WriteFile(quarantine pack): %v", err) - } - - if err := objectsRoot.WriteFile(path.Join("pack", "pack-a.pack"), []byte(payload), 0o644); err != nil { - t.Fatalf("WriteFile(permanent pack): %v", err) - } - - if err := svc.promoteQuarantine(quarantineName, quarantineRoot); err != nil { t.Fatalf("promoteQuarantine: %v", err) } } diff --git a/receivepack/internal/service/run_hook.go b/receivepack/internal/service/run_hook.go new file mode 100644 index 00000000..cf934664 --- /dev/null +++ b/receivepack/internal/service/run_hook.go @@ -0,0 +1,73 @@ +package service + +import "context" + +func (service *Service) runHook( + ctx context.Context, + req *Request, + commands []Command, + quarantineName string, +) ( + allowedCommands []Command, + allowedIndices []int, + rejected map[int]string, + ok bool, + errText string, +) { + allowedCommands = append([]Command(nil), commands...) + + allowedIndices = make([]int, 0, len(commands)) + for index := range commands { + allowedIndices = append(allowedIndices, index) + } + + rejected = make(map[int]string) + if service.opts.Hook == nil { + return allowedCommands, allowedIndices, rejected, true, "" + } + + quarantinedObjects, err := service.openQuarantinedObjects(quarantineName) + if err != nil { + return nil, nil, nil, false, err.Error() + } + + defer func() { + _ = quarantinedObjects.Close() + }() + + decisions, err := service.opts.Hook(ctx, HookRequest{ + Refs: service.opts.Refs, + ExistingObjects: service.opts.ExistingObjects, + QuarantinedObjects: quarantinedObjects, + Updates: buildHookUpdates(commands), + PushOptions: append([]string(nil), req.PushOptions...), + }) + if err != nil { + return nil, nil, nil, false, err.Error() + } + + if len(decisions) != len(commands) { + return nil, nil, nil, false, "hook returned wrong number of update decisions" + } + + allowedCommands = allowedCommands[:0] + allowedIndices = allowedIndices[:0] + + for index, decision := range decisions { + if decision.Accept { + allowedCommands = append(allowedCommands, commands[index]) + allowedIndices = append(allowedIndices, index) + + continue + } + + message := decision.Message + if message == "" { + message = "rejected by hook" + } + + rejected[index] = message + } + + return allowedCommands, allowedIndices, rejected, true, "" +} |
