v2.31.0-dev8 ec8c5b9

kernel.go RawHistoryFolder

1package app
2
3import (
4	"context"
5	"fmt"
6	"io"
7
8	"github.com/bnema/zerowrap"
9
10	"github.com/bnema/gordon/internal/boundaries/in"
11	configusecase "github.com/bnema/gordon/internal/usecase/config"
12	secretsusecase "github.com/bnema/gordon/internal/usecase/secrets"
13)
14
15// Kernel provides in-process service access for local CLI execution.
16//
17// It intentionally does not start HTTP servers or register signal handlers.
18type Kernel struct {
19	authEnabled  bool
20	configSvc    in.ConfigService
21	secretSvc    in.SecretService
22	containerSvc in.ContainerService
23	backupSvc    in.BackupService
24	registrySvc  in.RegistryService
25	healthSvc    in.HealthService
26	logSvc       in.LogService
27	volumeSvc    in.VolumeService
28	cleanup      func()
29}
30
31// NewKernel initializes local services without starting server listeners.
32func NewKernel(configPath string) (*Kernel, error) {
33	return newKernel(configPath, initLogger)
34}
35
36// NewKernelQuiet initializes local services without emitting console logs.
37func NewKernelQuiet(configPath string) (*Kernel, error) {
38	return newKernel(configPath, quietInitLogger)
39}
40
41type kernelLoggerInit func(cfg Config) (zerowrap.Logger, func(), error)
42
43func newKernel(configPath string, initLog kernelLoggerInit) (*Kernel, error) {
44	ctx := context.Background()
45	v, cfg, err := initConfig(configPath)
46	if err != nil {
47		return nil, err
48	}
49
50	log, cleanup, err := initLog(cfg)
51	if err != nil {
52		return nil, err
53	}
54	if cleanup == nil {
55		cleanup = func() {}
56	}
57
58	ctx = zerowrap.WithCtx(ctx, log)
59
60	// Prefer full service wiring so local CLI can execute the same operations
61	// as remote mode without going through HTTP admin endpoints.
62	if svc, fullErr := createServices(ctx, v, cfg, log); fullErr == nil {
63		return &Kernel{
64			authEnabled:  cfg.Auth.Enabled,
65			configSvc:    svc.configSvc,
66			secretSvc:    svc.secretSvc,
67			containerSvc: svc.containerSvc,
68			backupSvc:    svc.backupSvc,
69			registrySvc:  svc.registrySvc,
70			healthSvc:    svc.healthSvc,
71			logSvc:       svc.logSvc,
72			volumeSvc:    svc.volumeSvc,
73			cleanup:      cleanup,
74		}, nil
75	} else {
76		log.Warn().Err(fullErr).Msg("local kernel running in minimal mode")
77	}
78
79	configSvc := configusecase.NewService(v, nil)
80	if err := configSvc.Load(ctx); err != nil {
81		cleanup()
82		return nil, fmt.Errorf("failed to load configuration: %w", err)
83	}
84
85	_, _, _, domainSecretStore, err := createDomainSecretStore(cfg, log)
86	if err != nil {
87		cleanup()
88		return nil, fmt.Errorf("failed to create local secret store: %w", err)
89	}
90
91	secretSvc := secretsusecase.NewService(domainSecretStore, log, nil)
92
93	return &Kernel{
94		authEnabled: cfg.Auth.Enabled,
95		configSvc:   configSvc,
96		secretSvc:   secretSvc,
97		cleanup:     cleanup,
98	}, nil
99}
100
101func quietInitLogger(Config) (zerowrap.Logger, func(), error) {
102	return zerowrap.New(zerowrap.Config{Level: "disabled", Output: io.Discard}), func() {}, nil
103}
104
105func (k *Kernel) Close() error {
106	if k == nil || k.cleanup == nil {
107		return nil
108	}
109	k.cleanup()
110	return nil
111}
112
113func (k *Kernel) Config() in.ConfigService { return k.configSvc }
114
115func (k *Kernel) Secrets() in.SecretService { return k.secretSvc }
116
117func (k *Kernel) Container() in.ContainerService { return k.containerSvc }
118
119func (k *Kernel) Backup() in.BackupService { return k.backupSvc }
120
121func (k *Kernel) Registry() in.RegistryService { return k.registrySvc }
122
123func (k *Kernel) Health() in.HealthService { return k.healthSvc }
124
125func (k *Kernel) Logs() in.LogService { return k.logSvc }
126
127func (k *Kernel) Volumes() in.VolumeService { return k.volumeSvc }
128
129func (k *Kernel) AuthEnabled() bool { return k != nil && k.authEnabled }

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close