aboutsummaryrefslogtreecommitdiff
path: root/receivepack/hook.go
blob: 2a87276a9812ff170f9f606988d7d8ef99a95c0f (about) (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package receivepack

import (
	"context"
	"io"

	objectid "codeberg.org/lindenii/furgit/object/id"
	"codeberg.org/lindenii/furgit/object/storer"
	"codeberg.org/lindenii/furgit/receivepack/service"
	"codeberg.org/lindenii/furgit/ref/store"
)

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.
//
// Refs, ExistingObjects, and QuarantinedObjects are borrowed and are only
// valid for the duration of the hook call.
type HookRequest struct {
	Refs               refstore.ReadingStore
	ExistingObjects    objectstorer.Store
	QuarantinedObjects objectstorer.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. Hook borrows the data and stores in HookRequest only for
// the duration of the call.
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
	}
}