shithub: furgit

ref: 040b572d95e4ca27e1ada6113c405b8a1eb4a669
dir: /receivepack/hook.go/

View raw version
package receivepack

import (
	"context"
	"io"

	"codeberg.org/lindenii/furgit/objectid"
	"codeberg.org/lindenii/furgit/objectstore"
	"codeberg.org/lindenii/furgit/receivepack/service"
	"codeberg.org/lindenii/furgit/refstore"
)

type HookIO struct {
	Progress io.Writer
	Error    io.Writer
}

// RefUpdate is one requested reference update presented to a receive-pack hook.
type RefUpdate struct {
	Name  string
	OldID objectid.ObjectID
	NewID objectid.ObjectID
}

// UpdateDecision is one hook decision for a requested reference update.
type UpdateDecision struct {
	Accept  bool
	Message string
}

// HookRequest is the input presented to a receive-pack hook before quarantine
// promotion and ref updates.
type HookRequest struct {
	Refs               refstore.ReadingStore
	ExistingObjects    objectstore.Store
	QuarantinedObjects objectstore.Store
	Updates            []RefUpdate
	PushOptions        []string
	IO                 HookIO
}

// Hook decides whether each requested update should proceed.
//
// The hook runs after pack ingestion into quarantine and before quarantine
// promotion or ref updates. The returned decisions must have the same length as
// HookRequest.Updates.
type Hook func(context.Context, HookRequest) ([]UpdateDecision, error)

func translateHook(hook Hook) service.Hook {
	if hook == nil {
		return nil
	}

	return func(ctx context.Context, req service.HookRequest) ([]service.UpdateDecision, error) {
		translatedUpdates := make([]RefUpdate, 0, len(req.Updates))
		for _, update := range req.Updates {
			translatedUpdates = append(translatedUpdates, RefUpdate{
				Name:  update.Name,
				OldID: update.OldID,
				NewID: update.NewID,
			})
		}

		decisions, err := hook(ctx, HookRequest{
			Refs:               req.Refs,
			ExistingObjects:    req.ExistingObjects,
			QuarantinedObjects: req.QuarantinedObjects,
			Updates:            translatedUpdates,
			PushOptions:        append([]string(nil), req.PushOptions...),
			IO: HookIO{
				Progress: req.IO.Progress,
				Error:    req.IO.Error,
			},
		})
		if err != nil {
			return nil, err
		}

		out := make([]service.UpdateDecision, 0, len(decisions))
		for _, decision := range decisions {
			out = append(out, service.UpdateDecision{
				Accept:  decision.Accept,
				Message: decision.Message,
			})
		}

		return out, nil
	}
}