+1
-1
src/backend_fs.go
+1
-1
src/backend_fs.go
···
212
212
return filepath.Join(domain, ".frozen")
213
213
}
214
214
215
-
func (fs *FSBackend) checkDomainFrozen(_ctx context.Context, domain string) error {
215
+
func (fs *FSBackend) checkDomainFrozen(ctx context.Context, domain string) error {
216
216
if _, err := fs.siteRoot.Stat(domainFrozenMarkerName(domain)); err == nil {
217
217
return ErrDomainFrozen
218
218
} else if !errors.Is(err, os.ErrNotExist) {
+13
-2
src/log.go
+13
-2
src/log.go
···
4
4
"context"
5
5
"fmt"
6
6
"log/slog"
7
+
"os"
7
8
"runtime"
8
9
"time"
9
10
)
···
25
26
// skip [runtime.Callers, this method, method calling this method]
26
27
runtime.Callers(3, pcs[:])
27
28
28
-
r := slog.NewRecord(time.Now(), level, msg, pcs[0])
29
-
logger.Handler().Handle(ctx, r)
29
+
record := slog.NewRecord(time.Now(), level, msg, pcs[0])
30
+
logger.Handler().Handle(ctx, record)
30
31
}
31
32
32
33
func (l slogWithCtx) Print(ctx context.Context, v ...any) {
···
40
41
func (l slogWithCtx) Println(ctx context.Context, v ...any) {
41
42
l.log(ctx, slog.LevelInfo, fmt.Sprintln(v...))
42
43
}
44
+
45
+
func (l slogWithCtx) Fatalf(ctx context.Context, format string, v ...any) {
46
+
l.log(ctx, slog.LevelError, fmt.Sprintf(format, v...))
47
+
os.Exit(1)
48
+
}
49
+
50
+
func (l slogWithCtx) Fatalln(ctx context.Context, v ...any) {
51
+
l.log(ctx, slog.LevelError, fmt.Sprintln(v...))
52
+
os.Exit(1)
53
+
}
+70
-67
src/main.go
+70
-67
src/main.go
···
27
27
var fallback http.Handler
28
28
var backend Backend
29
29
30
-
func configureFeatures() (err error) {
30
+
func configureFeatures(ctx context.Context) (err error) {
31
31
if len(config.Features) > 0 {
32
-
log.Println("features:", strings.Join(config.Features, ", "))
32
+
logc.Println(ctx, "features:", strings.Join(config.Features, ", "))
33
33
}
34
34
return
35
35
}
36
36
37
-
func configureMemLimit() (err error) {
37
+
func configureMemLimit(ctx context.Context) (err error) {
38
38
// Avoid being OOM killed by not garbage collecting early enough.
39
39
memlimitBefore := datasize.ByteSize(debug.SetMemoryLimit(-1))
40
40
automemlimit.SetGoMemLimitWithOpts(
···
49
49
)
50
50
memlimitAfter := datasize.ByteSize(debug.SetMemoryLimit(-1))
51
51
if memlimitBefore == memlimitAfter {
52
-
log.Println("memlimit: now", memlimitBefore.HR())
52
+
logc.Println(ctx, "memlimit: now", memlimitBefore.HR())
53
53
} else {
54
-
log.Println("memlimit: was", memlimitBefore.HR(), "now", memlimitAfter.HR())
54
+
logc.Println(ctx, "memlimit: was", memlimitBefore.HR(), "now", memlimitAfter.HR())
55
55
}
56
56
return
57
57
}
58
58
59
-
func configureWildcards() (err error) {
59
+
func configureWildcards(_ context.Context) (err error) {
60
60
newWildcards, err := TranslateWildcards(config.Wildcard)
61
61
if err != nil {
62
62
return err
···
66
66
}
67
67
}
68
68
69
-
func configureFallback() (err error) {
69
+
func configureFallback(_ context.Context) (err error) {
70
70
if config.Fallback.ProxyTo != "" {
71
71
var fallbackURL *url.URL
72
72
fallbackURL, err = url.Parse(config.Fallback.ProxyTo)
···
91
91
return
92
92
}
93
93
94
-
func listen(name string, listen string) net.Listener {
94
+
func listen(ctx context.Context, name string, listen string) net.Listener {
95
95
if listen == "-" {
96
96
return nil
97
97
}
98
98
99
99
protocol, address, ok := strings.Cut(listen, "/")
100
100
if !ok {
101
-
log.Fatalf("%s: %s: malformed endpoint", name, listen)
101
+
logc.Fatalf(ctx, "%s: %s: malformed endpoint", name, listen)
102
102
}
103
103
104
104
listener, err := net.Listen(protocol, address)
105
105
if err != nil {
106
-
log.Fatalf("%s: %s\n", name, err)
106
+
logc.Fatalf(ctx, "%s: %s\n", name, err)
107
107
}
108
108
109
109
return listener
···
125
125
})
126
126
}
127
127
128
-
func serve(listener net.Listener, handler http.Handler) {
128
+
func serve(ctx context.Context, listener net.Listener, handler http.Handler) {
129
129
if listener != nil {
130
130
handler = panicHandler(handler)
131
131
···
135
135
if config.Feature("serve-h2c") {
136
136
server.Protocols.SetUnencryptedHTTP2(true)
137
137
}
138
-
log.Fatalln(server.Serve(listener))
138
+
logc.Fatalln(ctx, server.Serve(listener))
139
139
}
140
140
}
141
141
···
146
146
case 1:
147
147
return arg
148
148
default:
149
-
log.Fatalf("webroot argument must be either 'domain.tld' or 'domain.tld/dir")
149
+
logc.Fatalln(context.Background(),
150
+
"webroot argument must be either 'domain.tld' or 'domain.tld/dir")
150
151
return ""
151
152
}
152
153
}
···
158
159
} else {
159
160
writer, err = os.Create(flag.Arg(0))
160
161
if err != nil {
161
-
log.Fatalln(err)
162
+
logc.Fatalln(context.Background(), err)
162
163
}
163
164
}
164
165
return
···
178
179
}
179
180
180
181
func Main() {
182
+
ctx := context.Background()
183
+
181
184
flag.Usage = usage
182
185
printConfigEnvVars := flag.Bool("print-config-env-vars", false,
183
186
"print every recognized configuration environment variable and exit")
···
226
229
cliOperations += 1
227
230
}
228
231
if cliOperations > 1 {
229
-
log.Fatalln("-get-blob, -get-manifest, -get-archive, -update-site, -freeze, and -unfreeze are mutually exclusive")
232
+
logc.Fatalln(ctx, "-get-blob, -get-manifest, -get-archive, -update-site, -freeze, and -unfreeze are mutually exclusive")
230
233
}
231
234
232
235
if *configTomlPath != "" && *noConfig {
233
-
log.Fatalln("-no-config and -config are mutually exclusive")
236
+
logc.Fatalln(ctx, "-no-config and -config are mutually exclusive")
234
237
}
235
238
236
239
if *printConfigEnvVars {
···
243
246
*configTomlPath = "config.toml"
244
247
}
245
248
if config, err = Configure(*configTomlPath); err != nil {
246
-
log.Fatalln("config:", err)
249
+
logc.Fatalln(ctx, "config:", err)
247
250
}
248
251
249
252
if *printConfig {
···
255
258
defer FiniObservability()
256
259
257
260
if err = errors.Join(
258
-
configureFeatures(),
259
-
configureMemLimit(),
260
-
configureWildcards(),
261
-
configureFallback(),
261
+
configureFeatures(ctx),
262
+
configureMemLimit(ctx),
263
+
configureWildcards(ctx),
264
+
configureFallback(ctx),
262
265
); err != nil {
263
-
log.Fatalln(err)
266
+
logc.Fatalln(ctx, err)
264
267
}
265
268
266
269
switch {
267
270
case *runMigration != "":
268
271
if backend, err = CreateBackend(&config.Storage); err != nil {
269
-
log.Fatalln(err)
272
+
logc.Fatalln(ctx, err)
270
273
}
271
274
272
-
if err := RunMigration(context.Background(), *runMigration); err != nil {
273
-
log.Fatalln(err)
275
+
if err := RunMigration(ctx, *runMigration); err != nil {
276
+
logc.Fatalln(ctx, err)
274
277
}
275
278
276
279
case *getBlob != "":
277
280
if backend, err = CreateBackend(&config.Storage); err != nil {
278
-
log.Fatalln(err)
281
+
logc.Fatalln(ctx, err)
279
282
}
280
283
281
-
reader, _, _, err := backend.GetBlob(context.Background(), *getBlob)
284
+
reader, _, _, err := backend.GetBlob(ctx, *getBlob)
282
285
if err != nil {
283
-
log.Fatalln(err)
286
+
logc.Fatalln(ctx, err)
284
287
}
285
288
io.Copy(fileOutputArg(), reader)
286
289
287
290
case *getManifest != "":
288
291
if backend, err = CreateBackend(&config.Storage); err != nil {
289
-
log.Fatalln(err)
292
+
logc.Fatalln(ctx, err)
290
293
}
291
294
292
295
webRoot := webRootArg(*getManifest)
293
-
manifest, _, err := backend.GetManifest(context.Background(), webRoot, GetManifestOptions{})
296
+
manifest, _, err := backend.GetManifest(ctx, webRoot, GetManifestOptions{})
294
297
if err != nil {
295
-
log.Fatalln(err)
298
+
logc.Fatalln(ctx, err)
296
299
}
297
300
fmt.Fprintln(fileOutputArg(), ManifestDebugJSON(manifest))
298
301
299
302
case *getArchive != "":
300
303
if backend, err = CreateBackend(&config.Storage); err != nil {
301
-
log.Fatalln(err)
304
+
logc.Fatalln(ctx, err)
302
305
}
303
306
304
307
webRoot := webRootArg(*getArchive)
305
308
manifest, manifestMtime, err :=
306
-
backend.GetManifest(context.Background(), webRoot, GetManifestOptions{})
309
+
backend.GetManifest(ctx, webRoot, GetManifestOptions{})
307
310
if err != nil {
308
-
log.Fatalln(err)
311
+
logc.Fatalln(ctx, err)
309
312
}
310
-
CollectTar(context.Background(), fileOutputArg(), manifest, manifestMtime)
313
+
CollectTar(ctx, fileOutputArg(), manifest, manifestMtime)
311
314
312
315
case *updateSite != "":
313
316
if backend, err = CreateBackend(&config.Storage); err != nil {
314
-
log.Fatalln(err)
317
+
logc.Fatalln(ctx, err)
315
318
}
316
319
317
320
if flag.NArg() != 1 {
318
-
log.Fatalln("update source must be provided as the argument")
321
+
logc.Fatalln(ctx, "update source must be provided as the argument")
319
322
}
320
323
321
324
sourceURL, err := url.Parse(flag.Arg(0))
322
325
if err != nil {
323
-
log.Fatalln(err)
326
+
logc.Fatalln(ctx, err)
324
327
}
325
328
326
329
var result UpdateResult
327
330
if sourceURL.Scheme == "" {
328
331
file, err := os.Open(sourceURL.Path)
329
332
if err != nil {
330
-
log.Fatalln(err)
333
+
logc.Fatalln(ctx, err)
331
334
}
332
335
defer file.Close()
333
336
···
346
349
}
347
350
348
351
webRoot := webRootArg(*updateSite)
349
-
result = UpdateFromArchive(context.Background(), webRoot, contentType, file)
352
+
result = UpdateFromArchive(ctx, webRoot, contentType, file)
350
353
} else {
351
354
branch := "pages"
352
355
if sourceURL.Fragment != "" {
···
354
357
}
355
358
356
359
webRoot := webRootArg(*updateSite)
357
-
result = UpdateFromRepository(context.Background(), webRoot, sourceURL.String(), branch)
360
+
result = UpdateFromRepository(ctx, webRoot, sourceURL.String(), branch)
358
361
}
359
362
360
363
switch result.outcome {
361
364
case UpdateError:
362
-
log.Printf("error: %s\n", result.err)
365
+
logc.Printf(ctx, "error: %s\n", result.err)
363
366
os.Exit(2)
364
367
case UpdateTimeout:
365
-
log.Println("timeout")
368
+
logc.Println(ctx, "timeout")
366
369
os.Exit(1)
367
370
case UpdateCreated:
368
-
log.Println("created")
371
+
logc.Println(ctx, "created")
369
372
case UpdateReplaced:
370
-
log.Println("replaced")
373
+
logc.Println(ctx, "replaced")
371
374
case UpdateDeleted:
372
-
log.Println("deleted")
375
+
logc.Println(ctx, "deleted")
373
376
case UpdateNoChange:
374
-
log.Println("no-change")
377
+
logc.Println(ctx, "no-change")
375
378
}
376
379
377
380
case *freezeDomain != "" || *unfreezeDomain != "":
···
386
389
}
387
390
388
391
if backend, err = CreateBackend(&config.Storage); err != nil {
389
-
log.Fatalln(err)
392
+
logc.Fatalln(ctx, err)
390
393
}
391
394
392
-
if err = backend.FreezeDomain(context.Background(), domain, freeze); err != nil {
393
-
log.Fatalln(err)
395
+
if err = backend.FreezeDomain(ctx, domain, freeze); err != nil {
396
+
logc.Fatalln(ctx, err)
394
397
}
395
398
if freeze {
396
399
log.Println("frozen")
···
408
411
// The backend is not recreated (this is intentional as it allows preserving the cache).
409
412
OnReload(func() {
410
413
if newConfig, err := Configure(*configTomlPath); err != nil {
411
-
log.Println("config: reload err:", err)
414
+
logc.Println(ctx, "config: reload err:", err)
412
415
} else {
413
416
// From https://go.dev/ref/mem:
414
417
// > A read r of a memory location x holding a value that is not larger than
···
418
421
// > concurrent write.
419
422
config = newConfig
420
423
if err = errors.Join(
421
-
configureFeatures(),
422
-
configureMemLimit(),
423
-
configureWildcards(),
424
-
configureFallback(),
424
+
configureFeatures(ctx),
425
+
configureMemLimit(ctx),
426
+
configureWildcards(ctx),
427
+
configureFallback(ctx),
425
428
); err != nil {
426
429
// At this point the configuration is in an in-between, corrupted state, so
427
430
// the only reasonable choice is to crash.
428
-
log.Fatalln("config: reload fail:", err)
431
+
logc.Fatalln(ctx, "config: reload fail:", err)
429
432
} else {
430
-
log.Println("config: reload ok")
433
+
logc.Println(ctx, "config: reload ok")
431
434
}
432
435
}
433
436
})
···
436
439
// spends some time initializing (which the S3 backend does) a proxy like Caddy can race
437
440
// with git-pages on startup and return errors for requests that would have been served
438
441
// just 0.5s later.
439
-
pagesListener := listen("pages", config.Server.Pages)
440
-
caddyListener := listen("caddy", config.Server.Caddy)
441
-
metricsListener := listen("metrics", config.Server.Metrics)
442
+
pagesListener := listen(ctx, "pages", config.Server.Pages)
443
+
caddyListener := listen(ctx, "caddy", config.Server.Caddy)
444
+
metricsListener := listen(ctx, "metrics", config.Server.Metrics)
442
445
443
446
if backend, err = CreateBackend(&config.Storage); err != nil {
444
-
log.Fatalln(err)
447
+
logc.Fatalln(ctx, err)
445
448
}
446
449
backend = NewObservedBackend(backend)
447
450
448
-
go serve(pagesListener, ObserveHTTPHandler(http.HandlerFunc(ServePages)))
449
-
go serve(caddyListener, ObserveHTTPHandler(http.HandlerFunc(ServeCaddy)))
450
-
go serve(metricsListener, promhttp.Handler())
451
+
go serve(ctx, pagesListener, ObserveHTTPHandler(http.HandlerFunc(ServePages)))
452
+
go serve(ctx, caddyListener, ObserveHTTPHandler(http.HandlerFunc(ServeCaddy)))
453
+
go serve(ctx, metricsListener, promhttp.Handler())
451
454
452
455
if config.Insecure {
453
-
log.Println("serve: ready (INSECURE)")
456
+
logc.Println(ctx, "serve: ready (INSECURE)")
454
457
} else {
455
-
log.Println("serve: ready")
458
+
logc.Println(ctx, "serve: ready")
456
459
}
457
460
458
461
WaitForInterrupt()
459
-
log.Println("serve: exiting")
462
+
logc.Println(ctx, "serve: exiting")
460
463
}
461
464
}