aboutsummaryrefslogtreecommitdiff
path: root/receivepack/internal/service/execute.go
diff options
context:
space:
mode:
authorGravatar Runxi Yu2026-03-07 21:15:54 +0800
committerGravatar Runxi Yu2026-03-07 21:16:32 +0800
commitb82515530f10dfebbf99dca501890570f3466910 (patch)
tree9574dc49fa7239f7f0c131471f4a6708fd7041d5 /receivepack/internal/service/execute.go
parentreceivepack: Set permissions properly (diff)
signatureNo signature
receivepack: Add hooks
Diffstat (limited to 'receivepack/internal/service/execute.go')
-rw-r--r--receivepack/internal/service/execute.go96
1 files changed, 88 insertions, 8 deletions
diff --git a/receivepack/internal/service/execute.go b/receivepack/internal/service/execute.go
index ebba9003..8febfc79 100644
--- a/receivepack/internal/service/execute.go
+++ b/receivepack/internal/service/execute.go
@@ -8,13 +8,8 @@ import (
)
// Execute validates one receive-pack request, optionally ingests its pack into
-// quarantine, and plans ref updates.
-//
-// TODO: Invoke hook or policy callbacks to decide whether each planned update
-// should be allowed.
+// quarantine, runs the optional hook, and applies allowed ref updates.
func (service *Service) Execute(ctx context.Context, req *Request) (*Result, error) {
- _ = ctx
-
result := &Result{
Commands: make([]CommandResult, 0, len(req.Commands)),
}
@@ -95,6 +90,83 @@ 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)
+
+ continue
+ }
+
+ message := decision.Message
+ if message == "" {
+ message = "rejected by hook"
+ }
+
+ rejected[index] = 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
+ }
+ }
+
+ if len(allowedCommands) == 0 {
+ result.Commands = mergeCommandResults(req.Commands, rejected, nil, nil)
+
+ return result, nil
+ }
+
if req.PackExpected {
// Git migrates quarantined objects into permanent storage immediately
// before starting ref updates.
@@ -108,18 +180,26 @@ func (service *Service) Execute(ctx context.Context, req *Request) (*Result, err
}
if req.Atomic {
- err := service.applyAtomic(result, req.Commands)
+ subresult := &Result{}
+ err := service.applyAtomic(subresult, allowedCommands)
if err != nil {
return result, err
}
+ result.Commands = mergeCommandResults(req.Commands, rejected, subresult.Commands, allowedIndices)
+ result.Applied = subresult.Applied
+
return result, nil
}
- err = service.applyBatch(result, req.Commands)
+ subresult := &Result{}
+ err = service.applyBatch(subresult, allowedCommands)
if err != nil {
return result, err
}
+ result.Commands = mergeCommandResults(req.Commands, rejected, subresult.Commands, allowedIndices)
+ result.Applied = subresult.Applied
+
return result, nil
}