aboutsummaryrefslogtreecommitdiff
path: root/receivepack/internal/service/execute.go
diff options
context:
space:
mode:
authorGravatar Runxi Yu2026-03-07 14:24:22 +0800
committerGravatar Runxi Yu2026-03-07 17:17:14 +0800
commitdc7ce00cbe3c300caac3c13b6701240126b99e00 (patch)
treea00d516403fa7f7c5662937f7391e5d843b286d7 /receivepack/internal/service/execute.go
parentrefstore/files: Add new files backend (diff)
signatureNo signature
receivepack: Add service semantics thingy
Diffstat (limited to 'receivepack/internal/service/execute.go')
-rw-r--r--receivepack/internal/service/execute.go88
1 files changed, 88 insertions, 0 deletions
diff --git a/receivepack/internal/service/execute.go b/receivepack/internal/service/execute.go
new file mode 100644
index 00000000..b3d47d29
--- /dev/null
+++ b/receivepack/internal/service/execute.go
@@ -0,0 +1,88 @@
+package service
+
+import (
+ "context"
+ "log"
+
+ "codeberg.org/lindenii/furgit/format/pack/ingest"
+)
+
+// 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.
+// TODO: Apply planned ref updates with one atomic compare-and-swap ref
+// transaction once ref writing exists.
+func (service *Service) Execute(ctx context.Context, req *Request) (*Result, error) {
+ _ = ctx
+
+ result := &Result{
+ Commands: make([]CommandResult, 0, len(req.Commands)),
+ }
+
+ 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
+ }
+
+ defer func() {
+ _ = quarantineRoot.Close()
+ // TODO: Promote accepted quarantined objects into the permanent object
+ // store once atomic ref application exists.
+ _ = service.opts.ObjectsRoot.RemoveAll(quarantineName)
+ }()
+
+ ingested, err := ingest.Ingest(
+ req.Pack,
+ quarantineRoot,
+ 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 {
+ result.Planned = append(result.Planned, PlannedUpdate{
+ Name: command.Name,
+ OldID: command.OldID,
+ NewID: command.NewID,
+ Delete: isDelete(command),
+ })
+ }
+
+ fillCommandErrors(result, req.Commands, "ref updates not implemented yet")
+ log.Printf(
+ "receivepack: planned %d ref updates, but hook/policy checks and atomic ref writes are not implemented yet",
+ len(result.Planned),
+ )
+
+ return result, nil
+}