legacy_sandbox_test.go RawHistoryFolder

1package brokerconfig
2
3import (
4	"errors"
5	"fmt"
6	"path/filepath"
7)
8
9// ResolveLayout validates one offline root and derives its sandbox paths.
10//
11// The root must be absolute and clean, must not be the filesystem root, must
12// not contain a symlinked component, and must not equal, contain, or sit inside
13// any reserved production directory. Reserved directories are compared cleaned
14// and absolute; empty entries are ignored. The result is purely textual: no
15// directory is created here, and the caller secures the paths with
16// pkg/safedir.EnsurePrivate.
17func ResolveLayout(rawRoot string, reserved []string) (Layout, error) {
18	if rawRoot == "" {
19		return Layout{}, errors.New("brokerconfig: offline root is empty")
20	}
21	if !filepath.IsAbs(rawRoot) {
22		return Layout{}, fmt.Errorf("brokerconfig: offline root %q is not absolute", rawRoot)
23	}
24	if cleaned := filepath.Clean(rawRoot); cleaned != rawRoot {
25		return Layout{}, fmt.Errorf("brokerconfig: offline root %q is not clean", rawRoot)
26	}
27	if rawRoot == string(filepath.Separator) {
28		return Layout{}, errors.New("brokerconfig: offline root must not be the filesystem root")
29	}
30	if err := rejectSymlinkedComponents(rawRoot); err != nil {
31		return Layout{}, err
32	}
33	for _, other := range reserved {
34		if other == "" {
35			continue
36		}
37		other = filepath.Clean(other)
38		if !filepath.IsAbs(other) {
39			return Layout{}, fmt.Errorf("brokerconfig: reserved directory %q is not absolute", other)
40		}
41		if overlaps(rawRoot, other) {
42			return Layout{}, fmt.Errorf("brokerconfig: offline root %q overlaps production path %q", rawRoot, other)
43		}
44	}
45	runtime := filepath.Join(rawRoot, RuntimeDirName)
46	return Layout{
47		Root:     rawRoot,
48		Runtime:  runtime,
49		State:    filepath.Join(rawRoot, StateDirName),
50		Log:      filepath.Join(rawRoot, LogDirName),
51		Spawn:    filepath.Join(runtime, SpawnDirName),
52		reserved: append([]string(nil), reserved...),
53	}, nil
54}
55
56// Load reads and strictly validates the config marker inside layout.Root.
57//
58// The file must be a regular owner-only file that is not a symlink and is
59// bounded by MaxConfigBytes; the JSON must carry the exact marker, reject
60// unknown fields, duplicate keys, trailing data, invalid UTF-8, and excessive
61// nesting. Every registration is validated independently and endpoints must be
62// unique. Two registrations may alias one identical route (the same opaque
63// address) under different endpoints, but two that share the pool's exact
64// (authenticated identity, policy) pair must agree on that one route: the pool
65// keys a physical transport by the pair and ignores the address, so a second
66// route for the same pair is refused rather than silently dialed or ignored. A
67// missing or malformed config is an error, never an empty default.
68func Load(layout Layout) (*Config, error) {
69	return LoadPath(layout, filepath.Join(layout.Root, ConfigFileName))
70}
71
72// LocalMuxRoute returns the single provisioned Unix route a remote-side mux
73// helper bridges to. A configuration that provisions no Unix route, or more
74// than one, is refused: a helper must never guess which daemon-mux endpoint it
75// serves. The local binding's own carriage is deliberately excluded: the broker
76// dials it itself, and no remote-side helper ever bridges it.
77func (c *Config) LocalMuxRoute() (Route, error) {
78	if c == nil {
79		return Route{}, errors.New("brokerconfig: no configuration")
80	}
81	switch len(c.unixRoutes) {
82	case 0:
83		return Route{}, errors.New("brokerconfig: no Unix daemon-mux route is provisioned")
84	case 1:
85		return c.unixRoutes[0], nil
86	default:
87		return Route{}, errors.New("brokerconfig: more than one Unix daemon-mux route is provisioned")
88	}
89}

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 12

Tags 11

↑↓ moveenter openesc close