detached 7513520

refactor(ci): require the owner in CIConfig and use strconv in the ports

The config is the one place that defaults the owner. ports.itoa was a copy of
strconv.FormatInt.
commit
7513520567a99f94a8e3f7a1aed57d7de7623d16
author
brice · · 2026-10-12 19:17 UTC
parent
5b299eb
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +7 -19
M internal/ports/ci.go +2 -15
@@ -5,6 +5,7 @@ package ports
55 import (
66 "context"
77 "errors"
8+ "strconv"
89 "time"
910
1011 "bnema.dev/ermite/internal/domain"
@@ -125,26 +126,12 @@ type ErrCILogGap struct{ Count int64 }
125126
126127 // Error implements error.
127128 func (e *ErrCILogGap) Error() string {
128- return "out of range: the log holds lines up to the offset of " + itoa(e.Count)
129+ return "out of range: the log holds lines up to the offset of " + strconv.FormatInt(e.Count, 10)
129130 }
130131
131132 // Is makes errors.Is(err, domain.ErrOutOfRange) true.
132133 func (e *ErrCILogGap) Is(target error) bool { return target == domain.ErrOutOfRange }
133134
134-func itoa(n int64) string {
135- if n == 0 {
136- return "0"
137- }
138- var b [20]byte
139- i := len(b)
140- for n > 0 {
141- i--
142- b[i] = byte('0' + n%10)
143- n /= 10
144- }
145- return string(b[i:])
146-}
147-
148135 // CILogStore keeps the logs of the jobs: append-only files, one line per log
149136 // line, never in the metadata store. Lines are numbered from 0.
150137 type CILogStore interface {
@@ -5,6 +5,7 @@ package ports
5 import (5 import (
6 "context"6 "context"
7 "errors"7 "errors"
8+ "strconv"
8 "time"9 "time"
9 10
10 "bnema.dev/ermite/internal/domain"11 "bnema.dev/ermite/internal/domain"
@@ -125,26 +126,12 @@ type ErrCILogGap struct{ Count int64 }
125 126
126 // Error implements error.127 // Error implements error.
127 func (e *ErrCILogGap) Error() string {128 func (e *ErrCILogGap) Error() string {
128- return "out of range: the log holds lines up to the offset of " + itoa(e.Count)129+ return "out of range: the log holds lines up to the offset of " + strconv.FormatInt(e.Count, 10)
129 }130 }
130 131
131 // Is makes errors.Is(err, domain.ErrOutOfRange) true.132 // Is makes errors.Is(err, domain.ErrOutOfRange) true.
132 func (e *ErrCILogGap) Is(target error) bool { return target == domain.ErrOutOfRange }133 func (e *ErrCILogGap) Is(target error) bool { return target == domain.ErrOutOfRange }
133 134
134-func itoa(n int64) string {
135- if n == 0 {
136- return "0"
137- }
138- var b [20]byte
139- i := len(b)
140- for n > 0 {
141- i--
142- b[i] = byte('0' + n%10)
143- n /= 10
144- }
145- return string(b[i:])
146-}
147-
148 // CILogStore keeps the logs of the jobs: append-only files, one line per log135 // CILogStore keeps the logs of the jobs: append-only files, one line per log
149 // line, never in the metadata store. Lines are numbered from 0.136 // line, never in the metadata store. Lines are numbered from 0.
150 type CILogStore interface {137 type CILogStore interface {
M internal/usecase/ci.go +5 -4
@@ -42,7 +42,8 @@ const (
4242 )
4343
4444 // CIConfig holds what the CI use cases need. Store, Logs, Meta, Repos, Reader,
45-// Git, Diff, Bus and Clock are required.
45+// Git, Diff, Bus, Clock, Owner and PublicURL are required (the config supplies
46+// the defaults of Owner and Retention).
4647 type CIConfig struct {
4748 Store ports.CIStore
4849 Logs ports.CILogStore
@@ -92,9 +93,6 @@ func (c CIConfig) withDefaults() CIConfig {
9293 if c.TriggerQueue <= 0 {
9394 c.TriggerQueue = DefaultCITriggerQueue
9495 }
95- if c.Owner == "" {
96- c.Owner = "owner"
97- }
9896 return c
9997 }
10098
@@ -148,6 +146,9 @@ type pollRecord struct {
148146 // NewCI builds the CI use cases. ctx carries the logger; its cancellation is
149147 // not followed: call Start to run the workers and Close to stop them.
150148 func NewCI(ctx context.Context, cfg CIConfig) *CI {
149+ if cfg.Owner == "" {
150+ panic("usecase: CIConfig.Owner is required")
151+ }
151152 cfg = cfg.withDefaults()
152153 run, stop := context.WithCancel(context.WithoutCancel(ctx))
153154 return &CI{
@@ -42,7 +42,8 @@ const (
42 )42 )
43 43
44 // CIConfig holds what the CI use cases need. Store, Logs, Meta, Repos, Reader,44 // CIConfig holds what the CI use cases need. Store, Logs, Meta, Repos, Reader,
45-// Git, Diff, Bus and Clock are required.45+// Git, Diff, Bus, Clock, Owner and PublicURL are required (the config supplies
46+// the defaults of Owner and Retention).
46 type CIConfig struct {47 type CIConfig struct {
47 Store ports.CIStore48 Store ports.CIStore
48 Logs ports.CILogStore49 Logs ports.CILogStore
@@ -92,9 +93,6 @@ func (c CIConfig) withDefaults() CIConfig {
92 if c.TriggerQueue <= 0 {93 if c.TriggerQueue <= 0 {
93 c.TriggerQueue = DefaultCITriggerQueue94 c.TriggerQueue = DefaultCITriggerQueue
94 }95 }
95- if c.Owner == "" {
96- c.Owner = "owner"
97- }
98 return c96 return c
99 }97 }
100 98
@@ -148,6 +146,9 @@ type pollRecord struct {
148 // NewCI builds the CI use cases. ctx carries the logger; its cancellation is146 // NewCI builds the CI use cases. ctx carries the logger; its cancellation is
149 // not followed: call Start to run the workers and Close to stop them.147 // not followed: call Start to run the workers and Close to stop them.
150 func NewCI(ctx context.Context, cfg CIConfig) *CI {148 func NewCI(ctx context.Context, cfg CIConfig) *CI {
149+ if cfg.Owner == "" {
150+ panic("usecase: CIConfig.Owner is required")
151+ }
151 cfg = cfg.withDefaults()152 cfg = cfg.withDefaults()
152 run, stop := context.WithCancel(context.WithoutCancel(ctx))153 run, stop := context.WithCancel(context.WithoutCancel(ctx))
153 return &CI{154 return &CI{

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 2

Tags 45

↑↓ moveenter openesc close