package receivepack import ( "context" "io" "codeberg.org/lindenii/furgit/common/iowrap" common "codeberg.org/lindenii/furgit/network/protocol/v0v1/server" protoreceive "codeberg.org/lindenii/furgit/network/protocol/v0v1/server/receivepack" "codeberg.org/lindenii/furgit/network/receivepack/service" ) // TODO: Some more designing to do. In particular, we'd like to have access to // commit graphs and stored object abstractions and such here, especially because // hooks might want to access full repos, but we risk creating // circular dependencies if we import repository/ here. Might need an interface-ish // design, but that risks being over-complicated. // Theoretically we could also just give the hooks an os.Root but that // feels a bit ugly. // ReceivePack serves one receive-pack session over r/w. // // Labels: Deps-Borrowed. func ReceivePack( ctx context.Context, w iowrap.WriteFlusher, r io.Reader, opts Options, ) error { err := validateOptions(opts) if err != nil { return err } version := parseVersion(opts.GitProtocol) base := common.NewSession(r, w, common.Options{ Version: version, Algorithm: opts.Algorithm, }) agent := opts.Agent if agent == "" { agent = defaultAgent() } sessionID := opts.SessionID if sessionID == "" { sessionID = defaultSessionID() } pushCertNonce := opts.PushCertNonce if pushCertNonce == "" { pushCertNonce = defaultPushCertNonce() } protoSession := protoreceive.NewSession(base, protoreceive.Capabilities{ ReportStatus: true, ReportStatusV2: true, DeleteRefs: true, SideBand64K: true, Quiet: true, Atomic: true, OfsDelta: true, PushOptions: true, PushCertNonce: pushCertNonce, SessionID: sessionID, ObjectFormat: opts.Algorithm, Agent: agent, }) refs, err := advertisedRefs(opts) if err != nil { return err } err = protoSession.AdvertiseRefs(common.Advertisement{Refs: refs}) if err != nil { return err } err = base.Flush() if err != nil { return err } req, err := protoSession.ReadRequest() if err != nil { return err } progress := protoSession.ProgressWriter() if req.Capabilities.Quiet { progress = iowrap.NopFlush(io.Discard) } serviceReq := &service.Request{ Commands: translateCommands(req.Commands), PushOptions: append([]string(nil), req.PushOptions...), Atomic: req.Capabilities.Atomic, PackExpected: req.PackExpected, Pack: r, } svc := service.New(service.Options{ Refs: opts.Refs, ExistingObjects: opts.ExistingObjects, ObjectIngress: opts.ObjectIngress, CommitGraph: opts.CommitGraph, Progress: progress, Hook: translateHook(opts.Hook), HookIO: service.HookIO{ Progress: progress, Error: protoSession.ErrorWriter(), }, }) result, err := svc.Execute(ctx, serviceReq) if err != nil { return err } protoResult := translateResult(result) if req.Capabilities.ReportStatusV2 { err = protoSession.WriteReportStatusV2(protoResult) if err != nil { return err } } else if req.Capabilities.ReportStatus { err = protoSession.WriteReportStatus(protoResult) if err != nil { return err } } return base.Flush() }