ref: 5b8941986d4c3f398fc1fc2d1314e80510be346c
parent: 344d0c4d3c968506f5641da40fce581ea5bcdbbc
author: Runxi Yu <me@runxiyu.org>
date: Sat Mar 7 16:21:58 EST 2026
receivepack: Fix lint
--- a/receivepack/int_test.go
+++ b/receivepack/int_test.go
@@ -52,7 +52,8 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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")}
})
--- a/receivepack/internal/service/apply.go
+++ b/receivepack/internal/service/apply.go
@@ -15,6 +15,7 @@
err = queueWriteTransaction(tx, command)
if err != nil {_ = tx.Abort()
+
fillCommandErrors(result, commands, err.Error())
return nil
--- a/receivepack/internal/service/execute.go
+++ b/receivepack/internal/service/execute.go
@@ -3,8 +3,6 @@
import (
"context"
"os"
-
- "codeberg.org/lindenii/furgit/format/pack/ingest"
)
// Execute validates one receive-pack request, optionally ingests its pack into
@@ -13,6 +11,7 @@
result := &Result{Commands: make([]CommandResult, 0, len(req.Commands)),
}
+
var (
quarantineName string
quarantineRoot *os.Root
@@ -19,62 +18,16 @@
err error
)
- if req.PackExpected {- if req.Pack == nil {- result.UnpackError = "missing pack stream"
- fillCommandErrors(result, req.Commands, "missing pack stream")
+ quarantineName, quarantineRoot, ok := service.ingestQuarantine(result, req.Commands, req)
+ if !ok {+ return result, nil
+ }
- 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
- }
-
+ 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 @@
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)
+ allowedCommands, allowedIndices, rejected, ok, errText := service.runHook(
+ ctx,
+ req,
+ req.Commands,
+ quarantineName,
+ )
+ if !ok {+ fillCommandErrors(result, req.Commands, errText)
+
+ return result, nil
}
- 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)
-
- continue
- }
-
- 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 @@
if req.Atomic { subresult := &Result{}+
err := service.applyAtomic(subresult, allowedCommands)
if err != nil {return result, err
@@ -193,6 +102,7 @@
}
subresult := &Result{}+
err = service.applyBatch(subresult, allowedCommands)
if err != nil {return result, err
--- /dev/null
+++ b/receivepack/internal/service/ingest_quarantine.go
@@ -1,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
+}
--- a/receivepack/internal/service/quarantine.go
+++ b/receivepack/internal/service/quarantine.go
@@ -169,9 +169,10 @@
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)
@@ -178,7 +179,7 @@
}
err = renameErr
- } else {+ default:
_ = root.Remove(src)
return statErr
--- a/receivepack/internal/service/quarantine_test.go
+++ b/receivepack/internal/service/quarantine_test.go
@@ -1,5 +1,7 @@
-package service
+package service //nolint:testpackage
+// because we need access to quarantine internals
+
import (
"os"
"path"
@@ -9,167 +11,161 @@
"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)
+ fx := newQuarantineFixture(t, Options{+ PromotedObjectPermissions: &PromotedObjectPermissions{+ DirMode: 0o751,
+ FileMode: 0o640,
+ },
+ })
+
+ err := fx.quarantineRoot.Mkdir("ab", 0o700) if err != nil {- t.Fatalf("os.OpenRoot: %v", err)+ t.Fatalf("Mkdir(ab): %v", err)}
- t.Cleanup(func() {- _ = objectsRoot.Close()
- })
-
- svc := New(Options{- Algorithm: objectid.AlgorithmSHA1,
- ExistingObjects: memory.New(objectid.AlgorithmSHA1),
- ObjectsRoot: objectsRoot,
- })
-
- quarantineName, quarantineRoot, err := svc.createQuarantineRoot()
+ err = fx.quarantineRoot.WriteFile(path.Join("ab", "cdef"), []byte("payload"), 0o600) if err != nil {- t.Fatalf("createQuarantineRoot: %v", err)+ t.Fatalf("WriteFile(quarantine loose): %v", err)}
- t.Cleanup(func() {- _ = quarantineRoot.Close()
- _ = objectsRoot.RemoveAll(quarantineName)
- })
-
- 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 TestPromoteQuarantineAcceptsMatchingExistingPackFile(t *testing.T) {t.Parallel()
- objectsDir := t.TempDir()
- objectsRoot, err := os.OpenRoot(objectsDir)
- if err != nil {- t.Fatalf("os.OpenRoot: %v", err)- }
+ fx := newQuarantineFixture(t, Options{})+ writeMatchingPromotedFile(t, fx.quarantineRoot, fx.objectsRoot, "pack", "pack-a.pack", "identical pack bytes")
- t.Cleanup(func() {- _ = objectsRoot.Close()
- })
-
- svc := New(Options{- Algorithm: objectid.AlgorithmSHA1,
- ExistingObjects: memory.New(objectid.AlgorithmSHA1),
- ObjectsRoot: objectsRoot,
- })
-
- 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)}
}
--- /dev/null
+++ b/receivepack/internal/service/run_hook.go
@@ -1,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, ""
+}
--
⑨