detached 657aee7

fix(app) capture server refs for graceful shutdown, fix lint issues

- Capture both return values from startServer so registrySrv/proxySrv
  are defined for gracefulShutdown.
- Tighten self-signed cert file permissions from 0644 to 0600 (gosec G302).
- Extract waitForShutdown to reduce runServers cyclomatic complexity (gocyclo).
- Fix pre-existing gofmt indentation on DataDir field.
commit
657aee7bf8b1c86e5235622f53f10ce9e1fc8b8e
author
brice · · 2026-02-07 06:10 UTC
committer
brice · · 2026-02-07 06:11 UTC
parent
c3329b4
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
refs
tag: v2.12.0
1 file · +15 -10
M internal/app/run.go +15 -10
@@ -78,7 +78,7 @@ type Config struct {
7878 TLSPort int `mapstructure:"tls_port"`
7979 TLSCertFile string `mapstructure:"tls_cert_file"`
8080 TLSKeyFile string `mapstructure:"tls_key_file"`
81- DataDir string `mapstructure:"data_dir"`
81+ DataDir string `mapstructure:"data_dir"`
8282 MaxProxyBodySize string `mapstructure:"max_proxy_body_size"` // e.g., "512MB", "1GB"
8383 MaxBlobChunkSize string `mapstructure:"max_blob_chunk_size"` // e.g., "512MB", "1GB"
8484 MaxProxyResponseSize string `mapstructure:"max_proxy_response_size"` // e.g., "1GB", "0" for no limit
@@ -298,7 +298,7 @@ func generateSelfSignedTLSCert(certPath, keyPath, domainName string) error {
298298 return err
299299 }
300300
301- certOut, err := os.OpenFile(certPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
301+ certOut, err := os.OpenFile(certPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
302302 if err != nil {
303303 return err
304304 }
@@ -1277,8 +1277,8 @@ func runServers(ctx context.Context, cfg Config, registryHandler, proxyHandler h
12771277
12781278 errChan := make(chan error, 3)
12791279
1280- registryReady := startServer(fmt.Sprintf(":%d", cfg.Server.RegistryPort), registryHandler, "registry", errChan, log)
1281- proxyReady := startServer(fmt.Sprintf(":%d", cfg.Server.Port), proxyHandler, "proxy", errChan, log)
1280+ registrySrv, registryReady := startServer(fmt.Sprintf(":%d", cfg.Server.RegistryPort), registryHandler, "registry", errChan, log)
1281+ proxySrv, proxyReady := startServer(fmt.Sprintf(":%d", cfg.Server.Port), proxyHandler, "proxy", errChan, log)
12821282 var tlsReady <-chan struct{}
12831283 if cfg.Server.TLSEnabled {
12841284 if cfg.Server.TLSCertFile == "" || cfg.Server.TLSKeyFile == "" {
@@ -1326,10 +1326,19 @@ func runServers(ctx context.Context, cfg Config, registryHandler, proxyHandler h
13261326 // Auto-start after servers are listening (registry port is now bound).
13271327 syncAndAutoStart(ctx, svc, log)
13281328
1329+ waitForShutdown(ctx, errChan, reloadChan, deployChan, eventBus, log)
1330+ gracefulShutdown(registrySrv, proxySrv, containerSvc, log)
1331+ return nil
1332+}
1333+
1334+// waitForShutdown blocks on the event loop, handling server errors and
1335+// Unix signals (reload, deploy, shutdown) until the context is cancelled.
1336+func waitForShutdown(ctx context.Context, errChan <-chan error, reloadChan, deployChan <-chan os.Signal, eventBus out.EventBus, log zerowrap.Logger) {
13291337 for {
13301338 select {
13311339 case err := <-errChan:
1332- return err
1340+ log.Error().Err(err).Msg("server error")
1341+ return
13331342 case <-reloadChan:
13341343 log.Info().Msg("reload signal received (SIGUSR1)")
13351344 if err := eventBus.Publish(domain.EventManualReload, nil); err != nil {
@@ -1348,13 +1357,9 @@ func runServers(ctx context.Context, cfg Config, registryHandler, proxyHandler h
13481357 }
13491358 case <-ctx.Done():
13501359 log.Info().Msg("shutdown signal received")
1351- goto shutdown
1360+ return
13521361 }
13531362 }
1354-
1355-shutdown:
1356- gracefulShutdown(registrySrv, proxySrv, containerSvc, log)
1357- return nil
13581363 }
13591364
13601365 // gracefulShutdown stops HTTP servers with a 30s timeout, then shuts down
@@ -78,7 +78,7 @@ type Config struct {
78 TLSPort int `mapstructure:"tls_port"`78 TLSPort int `mapstructure:"tls_port"`
79 TLSCertFile string `mapstructure:"tls_cert_file"`79 TLSCertFile string `mapstructure:"tls_cert_file"`
80 TLSKeyFile string `mapstructure:"tls_key_file"`80 TLSKeyFile string `mapstructure:"tls_key_file"`
81- DataDir string `mapstructure:"data_dir"`81+ DataDir string `mapstructure:"data_dir"`
82 MaxProxyBodySize string `mapstructure:"max_proxy_body_size"` // e.g., "512MB", "1GB"82 MaxProxyBodySize string `mapstructure:"max_proxy_body_size"` // e.g., "512MB", "1GB"
83 MaxBlobChunkSize string `mapstructure:"max_blob_chunk_size"` // e.g., "512MB", "1GB"83 MaxBlobChunkSize string `mapstructure:"max_blob_chunk_size"` // e.g., "512MB", "1GB"
84 MaxProxyResponseSize string `mapstructure:"max_proxy_response_size"` // e.g., "1GB", "0" for no limit84 MaxProxyResponseSize string `mapstructure:"max_proxy_response_size"` // e.g., "1GB", "0" for no limit
@@ -298,7 +298,7 @@ func generateSelfSignedTLSCert(certPath, keyPath, domainName string) error {
298 return err298 return err
299 }299 }
300 300
301- certOut, err := os.OpenFile(certPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)301+ certOut, err := os.OpenFile(certPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
302 if err != nil {302 if err != nil {
303 return err303 return err
304 }304 }
@@ -1277,8 +1277,8 @@ func runServers(ctx context.Context, cfg Config, registryHandler, proxyHandler h
1277 1277
1278 errChan := make(chan error, 3)1278 errChan := make(chan error, 3)
1279 1279
1280- registryReady := startServer(fmt.Sprintf(":%d", cfg.Server.RegistryPort), registryHandler, "registry", errChan, log)1280+ registrySrv, registryReady := startServer(fmt.Sprintf(":%d", cfg.Server.RegistryPort), registryHandler, "registry", errChan, log)
1281- proxyReady := startServer(fmt.Sprintf(":%d", cfg.Server.Port), proxyHandler, "proxy", errChan, log)1281+ proxySrv, proxyReady := startServer(fmt.Sprintf(":%d", cfg.Server.Port), proxyHandler, "proxy", errChan, log)
1282 var tlsReady <-chan struct{}1282 var tlsReady <-chan struct{}
1283 if cfg.Server.TLSEnabled {1283 if cfg.Server.TLSEnabled {
1284 if cfg.Server.TLSCertFile == "" || cfg.Server.TLSKeyFile == "" {1284 if cfg.Server.TLSCertFile == "" || cfg.Server.TLSKeyFile == "" {
@@ -1326,10 +1326,19 @@ func runServers(ctx context.Context, cfg Config, registryHandler, proxyHandler h
1326 // Auto-start after servers are listening (registry port is now bound).1326 // Auto-start after servers are listening (registry port is now bound).
1327 syncAndAutoStart(ctx, svc, log)1327 syncAndAutoStart(ctx, svc, log)
1328 1328
1329+ waitForShutdown(ctx, errChan, reloadChan, deployChan, eventBus, log)
1330+ gracefulShutdown(registrySrv, proxySrv, containerSvc, log)
1331+ return nil
1332+}
1333+
1334+// waitForShutdown blocks on the event loop, handling server errors and
1335+// Unix signals (reload, deploy, shutdown) until the context is cancelled.
1336+func waitForShutdown(ctx context.Context, errChan <-chan error, reloadChan, deployChan <-chan os.Signal, eventBus out.EventBus, log zerowrap.Logger) {
1329 for {1337 for {
1330 select {1338 select {
1331 case err := <-errChan:1339 case err := <-errChan:
1332- return err1340+ log.Error().Err(err).Msg("server error")
1341+ return
1333 case <-reloadChan:1342 case <-reloadChan:
1334 log.Info().Msg("reload signal received (SIGUSR1)")1343 log.Info().Msg("reload signal received (SIGUSR1)")
1335 if err := eventBus.Publish(domain.EventManualReload, nil); err != nil {1344 if err := eventBus.Publish(domain.EventManualReload, nil); err != nil {
@@ -1348,13 +1357,9 @@ func runServers(ctx context.Context, cfg Config, registryHandler, proxyHandler h
1348 }1357 }
1349 case <-ctx.Done():1358 case <-ctx.Done():
1350 log.Info().Msg("shutdown signal received")1359 log.Info().Msg("shutdown signal received")
1351- goto shutdown1360+ return
1352 }1361 }
1353 }1362 }
1354-
1355-shutdown:
1356- gracefulShutdown(registrySrv, proxySrv, containerSvc, log)
1357- return nil
1358 }1363 }
1359 1364
1360 // gracefulShutdown stops HTTP servers with a 30s timeout, then shuts down1365 // gracefulShutdown stops HTTP servers with a 30s timeout, then shuts down

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