MCPcopy Create free account
hub / github.com/codehamr/codehamr / writeYAML

Function writeYAML

internal/config/config.go:269–327  ·  view source on GitHub ↗
(path string, v any)

Source from the content-addressed store, hash-verified

267 return writeYAML(filepath.Join(c.Dir, "config.yaml"), c)
268}
269
270func writeYAML(path string, v any) error {
271 b, err := yaml.Marshal(v)
272 if err != nil {
273 return err
274 }
275 // Re-prepended every Save since yaml.Marshal drops free-form comments, the
276 // only place a hint survives. The sandbox line catches the top first-run
277 // footgun: in a devcontainer/WSL2 with Ollama on the host, `localhost`
278 // doesn't reach the host and yields a baffling "connection refused". Native
279 // users aren't affected, hence sandbox-vs-host framing over an OS-specific one.
280 header := []byte(`# codehamr configuration
281#
282# Running codehamr in a devcontainer / WSL2 with Ollama on the host:
283# swap 'http://localhost:11434' with 'http://host.docker.internal:11434' below.
284#
285# Keys: ` + "`key: ${MY_KEY}`" + ` expands the env var at runtime, so the reference (not
286# the secret) round-trips on Save. Literal keys still work for backward compat.
287#
288# context_size is what codehamr packs to - set it to your server's ACTUAL window,
289# not the model's theoretical max. For Ollama that's OLLAMA_CONTEXT_LENGTH (or a
290# Modelfile 'PARAMETER num_ctx'); too high and the server silently drops the
291# oldest messages. More VRAM lets you raise both together.
292#
293# The seeded 262144 is qwen3.8:27b's full 256k window; your server only delivers
294# it if told to (start Ollama with OLLAMA_CONTEXT_LENGTH=262144). Serving less?
295# Lower 'context_size' here to match.
296
297`)
298 // Write to a sibling temp then rename over config.yaml. Rename is atomic
299 // within the directory, so a crash, signal, or full disk mid-write can never
300 // leave a truncated config.yaml, which Bootstrap's strict decode would fatal
301 // on, bricking the next launch until the file is hand-deleted. Mirrors
302 // internal/update's promote-by-rename. os.CreateTemp makes the temp 0o600 and
303 // rename installs that fresh inode in place, so this also closes the
304 // upgrade-path leak the old in-place write needed a trailing Chmod for:
305 // config.yaml carries the hamrpass key, and only the project owner should
306 // read it.
307 tmp, err := os.CreateTemp(filepath.Dir(path), ".config-*.yaml")
308 if err != nil {
309 return err
310 }
311 tmpPath := tmp.Name()
312 defer os.Remove(tmpPath) // no-op after a successful rename; cleans up early returns
313 if _, err := tmp.Write(append(header, b...)); err != nil {
314 tmp.Close()
315 return err
316 }
317 // Sync before the rename: rename is metadata-only, so a power loss right
318 // after Save could otherwise journal the rename ahead of the data and
319 // leave the truncated config.yaml the crash-safety above promises away.
320 if err := tmp.Sync(); err != nil {
321 tmp.Close()
322 return err
323 }
324 if err := tmp.Close(); err != nil {
325 return err
326 }

Callers 2

BootstrapFunction · 0.85
SaveMethod · 0.85

Calls 1

WriteMethod · 0.80

Tested by

no test coverage detected