diff options
| author | 2026-03-07 21:15:54 +0800 | |
|---|---|---|
| committer | 2026-03-07 21:16:32 +0800 | |
| commit | b82515530f10dfebbf99dca501890570f3466910 (patch) | |
| tree | 9574dc49fa7239f7f0c131471f4a6708fd7041d5 /receivepack/internal/service/execute.go | |
| parent | receivepack: Set permissions properly (diff) | |
| signature | No signature | |
receivepack: Add hooks
Diffstat (limited to 'receivepack/internal/service/execute.go')
| -rw-r--r-- | receivepack/internal/service/execute.go | 96 |
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 } |
