diff options
| author | 2026-03-26 09:14:59 +0000 | |
|---|---|---|
| committer | 2026-03-26 09:14:59 +0000 | |
| commit | 3d25bda9d5da6814661828adabe8a09f9d01aefb (patch) | |
| tree | d034e28079333f85e5d7b96d921282eddd4798d6 /network/receivepack/service/execute.go | |
| parent | object/id: Empty tree (diff) | |
| signature | No signature | |
network/receivepack: Rename from receivepack
Diffstat (limited to 'network/receivepack/service/execute.go')
| -rw-r--r-- | network/receivepack/service/execute.go | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/network/receivepack/service/execute.go b/network/receivepack/service/execute.go new file mode 100644 index 00000000..9f373e0d --- /dev/null +++ b/network/receivepack/service/execute.go @@ -0,0 +1,123 @@ +package service + +import ( + "context" + "os" + + "codeberg.org/lindenii/furgit/internal/utils" +) + +// Execute validates one receive-pack request, optionally ingests its pack into +// quarantine, runs the optional hook, and applies allowed ref updates. +func (service *Service) Execute(ctx context.Context, req *Request) (*Result, error) { + result := &Result{ + Commands: make([]CommandResult, 0, len(req.Commands)), + } + + var ( + quarantineName string + quarantineRoot *os.Root + err error + ) + + 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) + }() + } + + for _, command := range req.Commands { + result.Planned = append(result.Planned, PlannedUpdate{ + Name: command.Name, + OldID: command.OldID, + NewID: command.NewID, + Delete: isDelete(command), + }) + } + + if len(req.Commands) == 0 { + return result, nil + } + + allowedCommands, allowedIndices, rejected, ok, errText := service.runHook( + ctx, + req, + req.Commands, + quarantineName, + ) + if !ok { + fillCommandErrors(result, req.Commands, errText) + + return result, nil + } + + 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 && quarantineRoot != nil { + // Git migrates quarantined objects into permanent storage immediately + // before starting ref updates. + utils.BestEffortFprintf(service.opts.Progress, "promoting quarantine...\r") + + err = service.promoteQuarantine(quarantineName, quarantineRoot) + if err != nil { + utils.BestEffortFprintf(service.opts.Progress, "promoting quarantine: failed: %v.\n", err) + + result.UnpackError = err.Error() + fillCommandErrors(result, req.Commands, err.Error()) + + return result, nil + } + + utils.BestEffortFprintf(service.opts.Progress, "promoting quarantine: done.\n") + } + + if req.Atomic { + 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 + } + + 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 +} |
