refactor environment utilities and file tools
This commit is contained in:
parent
81933e5281
commit
aca5546ea5
|
|
@ -13,9 +13,9 @@ import (
|
|||
|
||||
"ollie/cmd/olliesrv/internal/agent"
|
||||
"ollie/cmd/olliesrv/internal/fs"
|
||||
"ollie/env"
|
||||
olog "ollie/log"
|
||||
"ollie/paths"
|
||||
"ollie/util"
|
||||
|
||||
"9fans.net/go/plan9/client"
|
||||
|
||||
|
|
@ -41,7 +41,7 @@ func main() {
|
|||
}
|
||||
|
||||
func runServer(sockPath string) {
|
||||
env.EnsureDefaults()
|
||||
util.EnsureEnv()
|
||||
|
||||
// Remove stale sockets from this and prior runs
|
||||
if _, err := os.Stat(sockPath); err == nil {
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ import (
|
|||
|
||||
"ollie/cmd/toolsrv/internal/registry"
|
||||
"ollie/cmd/toolsrv/internal/server"
|
||||
"ollie/env"
|
||||
lib9p "ollie/lib9p/client"
|
||||
"ollie/util"
|
||||
|
||||
olog "ollie/log"
|
||||
"ollie/paths"
|
||||
|
|
@ -73,7 +73,7 @@ func main() {
|
|||
defer sink.Flush()
|
||||
logger = sink.NewLogger("toolsrv")
|
||||
|
||||
env.EnsureDefaults()
|
||||
util.EnsureEnv()
|
||||
|
||||
// Prepend our bin dir to PATH so landrun is found (deployed alongside us).
|
||||
home, _ := os.UserHomeDir()
|
||||
|
|
|
|||
|
|
@ -1,52 +1,8 @@
|
|||
// Package env manages the daemon-global environment for ollie.
|
||||
// It loads $XDG_CONFIG_HOME/ollie/env and provides defaults.
|
||||
// Package env is retained for backward compatibility.
|
||||
// New code should import ollie/util.
|
||||
package env
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
import "ollie/util"
|
||||
|
||||
"ollie/paths"
|
||||
)
|
||||
|
||||
// EnsureDefaults loads $XDG_CONFIG_HOME/ollie/env and sets default values for any
|
||||
// OLLIE_* vars not already present in the process environment.
|
||||
func EnsureDefaults() {
|
||||
loadFile(filepath.Join(paths.CfgDir(), "env"))
|
||||
}
|
||||
|
||||
// loadFile reads KEY=VALUE pairs from path and sets any key not already
|
||||
// present in the environment. Lines beginning with # and blank lines are
|
||||
// ignored. Missing file is silently ignored.
|
||||
func loadFile(path string) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
sc := bufio.NewScanner(f)
|
||||
for sc.Scan() {
|
||||
line := strings.TrimSpace(sc.Text())
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
k, v, ok := strings.Cut(line, "=")
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
k = strings.TrimSpace(k)
|
||||
if i := strings.Index(v, " #"); i >= 0 {
|
||||
v = v[:i]
|
||||
}
|
||||
v = strings.TrimSpace(v)
|
||||
if len(v) >= 2 && (v[0] == '"' && v[len(v)-1] == '"' || v[0] == '\'' && v[len(v)-1] == '\'') {
|
||||
v = v[1 : len(v)-1]
|
||||
}
|
||||
if k != "" && os.Getenv(k) == "" {
|
||||
os.Setenv(k, paths.ExpandHome(os.ExpandEnv(v))) //nolint:errcheck
|
||||
}
|
||||
}
|
||||
}
|
||||
// EnsureDefaults loads the configured environment file and sets defaults.
|
||||
func EnsureDefaults() { util.EnsureEnv() }
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
package env
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEnsureDefaults(t *testing.T) {
|
||||
EnsureDefaults()
|
||||
}
|
||||
|
||||
func TestLoadFile(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
f := dir + "/env"
|
||||
os.WriteFile(f, []byte("OLLIE_TEST_LOAD=testval\n"), 0600)
|
||||
t.Setenv("OLLIE_TEST_LOAD", "")
|
||||
os.Unsetenv("OLLIE_TEST_LOAD")
|
||||
loadFile(f)
|
||||
if got := os.Getenv("OLLIE_TEST_LOAD"); got != "testval" {
|
||||
t.Errorf("loadFile: OLLIE_TEST_LOAD = %q, want testval", got)
|
||||
}
|
||||
}
|
||||
|
|
@ -36,12 +36,12 @@ type fidHandle struct {
|
|||
|
||||
var (
|
||||
handleMu sync.Mutex
|
||||
handles = make(map[C.int]*connHandle)
|
||||
handles = make(map[C.int]*connHandle)
|
||||
nextHandle C.int = 1
|
||||
|
||||
fidMu sync.Mutex
|
||||
fids = make(map[C.int]*fidHandle)
|
||||
nextFid C.int = 1
|
||||
fidMu sync.Mutex
|
||||
fids = make(map[C.int]*fidHandle)
|
||||
nextFid C.int = 1
|
||||
)
|
||||
|
||||
func allocHandle(h *connHandle) C.int {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import (
|
|||
// NewUUID generates a random UUIDv4 string.
|
||||
func NewUUID() string {
|
||||
var b [16]byte
|
||||
rand.Read(b[:]) //nolint:errcheck
|
||||
rand.Read(b[:]) //nolint:errcheck
|
||||
b[6] = (b[6] & 0x0f) | 0x40 // version 4
|
||||
b[8] = (b[8] & 0x3f) | 0x80 // variant 10xx
|
||||
return fmt.Sprintf("%08x-%04x-%04x-%04x-%012x",
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ import (
|
|||
)
|
||||
|
||||
type args struct {
|
||||
Path, Query string `json:"path"`
|
||||
Path string `json:"path"`
|
||||
Query string `json:"query"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
|
|
|||
|
|
@ -162,19 +162,126 @@ func Read(m map[string]any) (string, error) {
|
|||
}
|
||||
return status + "\n" + strings.TrimSuffix(b.String(), "\n"), nil
|
||||
}
|
||||
|
||||
type diffOp struct {
|
||||
kind byte
|
||||
line string
|
||||
}
|
||||
|
||||
func Diff(path, old, new string) string {
|
||||
a := strings.SplitAfter(old, "\n")
|
||||
b := strings.SplitAfter(new, "\n")
|
||||
oldLines := diffLines(old)
|
||||
newLines := diffLines(new)
|
||||
ops := diffOps(oldLines, newLines)
|
||||
|
||||
var changed []int
|
||||
for i, op := range ops {
|
||||
if op.kind != ' ' {
|
||||
changed = append(changed, i)
|
||||
}
|
||||
}
|
||||
if len(changed) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
const context = 3
|
||||
type hunk struct{ start, end int }
|
||||
var hunks []hunk
|
||||
for _, i := range changed {
|
||||
start := i - context
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
end := i + context + 1
|
||||
if len(hunks) > 0 && start <= hunks[len(hunks)-1].end {
|
||||
if end > hunks[len(hunks)-1].end {
|
||||
hunks[len(hunks)-1].end = end
|
||||
}
|
||||
continue
|
||||
}
|
||||
hunks = append(hunks, hunk{start: start, end: end})
|
||||
}
|
||||
for i := range hunks {
|
||||
if hunks[i].end > len(ops) {
|
||||
hunks[i].end = len(ops)
|
||||
}
|
||||
}
|
||||
|
||||
var out strings.Builder
|
||||
fmt.Fprintf(&out, "--- %s\n+++ %s\n", path, path)
|
||||
for _, x := range a {
|
||||
fmt.Fprintf(&out, "-%s", x)
|
||||
}
|
||||
for _, x := range b {
|
||||
fmt.Fprintf(&out, "+%s", x)
|
||||
for _, h := range hunks {
|
||||
oldStart, newStart := 1, 1
|
||||
for _, op := range ops[:h.start] {
|
||||
if op.kind != '+' {
|
||||
oldStart++
|
||||
}
|
||||
if op.kind != '-' {
|
||||
newStart++
|
||||
}
|
||||
}
|
||||
oldCount, newCount := 0, 0
|
||||
for _, op := range ops[h.start:h.end] {
|
||||
if op.kind != '+' {
|
||||
oldCount++
|
||||
}
|
||||
if op.kind != '-' {
|
||||
newCount++
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(&out, "@@ -%d,%d +%d,%d @@\n", oldStart, oldCount, newStart, newCount)
|
||||
for _, op := range ops[h.start:h.end] {
|
||||
out.WriteByte(op.kind)
|
||||
out.WriteString(op.line)
|
||||
if !strings.HasSuffix(op.line, "\n") {
|
||||
out.WriteByte('\n')
|
||||
}
|
||||
}
|
||||
}
|
||||
return out.String()
|
||||
}
|
||||
|
||||
func diffLines(s string) []string {
|
||||
if s == "" {
|
||||
return nil
|
||||
}
|
||||
lines := strings.SplitAfter(s, "\n")
|
||||
if lines[len(lines)-1] == "" {
|
||||
lines = lines[:len(lines)-1]
|
||||
}
|
||||
return lines
|
||||
}
|
||||
|
||||
func diffOps(a, b []string) []diffOp {
|
||||
lcs := make([][]int, len(a)+1)
|
||||
for i := range lcs {
|
||||
lcs[i] = make([]int, len(b)+1)
|
||||
}
|
||||
for i := len(a) - 1; i >= 0; i-- {
|
||||
for j := len(b) - 1; j >= 0; j-- {
|
||||
if a[i] == b[j] {
|
||||
lcs[i][j] = lcs[i+1][j+1] + 1
|
||||
} else if lcs[i+1][j] >= lcs[i][j+1] {
|
||||
lcs[i][j] = lcs[i+1][j]
|
||||
} else {
|
||||
lcs[i][j] = lcs[i][j+1]
|
||||
}
|
||||
}
|
||||
}
|
||||
ops := make([]diffOp, 0, len(a)+len(b)-lcs[0][0])
|
||||
for i, j := 0, 0; i < len(a) || j < len(b); {
|
||||
if i < len(a) && j < len(b) && a[i] == b[j] {
|
||||
ops = append(ops, diffOp{' ', a[i]})
|
||||
i++
|
||||
j++
|
||||
} else if j == len(b) || (i < len(a) && lcs[i+1][j] >= lcs[i][j+1]) {
|
||||
ops = append(ops, diffOp{'-', a[i]})
|
||||
i++
|
||||
} else {
|
||||
ops = append(ops, diffOp{'+', b[j]})
|
||||
j++
|
||||
}
|
||||
}
|
||||
return ops
|
||||
}
|
||||
func Edit(m map[string]any) (string, error) {
|
||||
p, e := Arg(m, "path")
|
||||
if e != nil {
|
||||
|
|
|
|||
|
|
@ -45,6 +45,27 @@ func TestWriteAndEdit(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
func TestDiffUsesContextHunks(t *testing.T) {
|
||||
old := "one\ntwo\nthree\nfour\nfive\nsix\nseven\neight\n"
|
||||
new := "one\ntwo\nTHREE\nfour\nfive\nsix\nseven\nEIGHT\n"
|
||||
got := Diff("x", old, new)
|
||||
want := "--- x\n+++ x\n@@ -1,8 +1,8 @@\n one\n two\n-three\n+THREE\n four\n five\n six\n seven\n-eight\n+EIGHT\n"
|
||||
if got != want {
|
||||
t.Fatalf("diff = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiffOmitsUnchangedFileContent(t *testing.T) {
|
||||
old := "line 1\nline 2\nline 3\nline 4\nline 5\nline 6\nline 7\nline 8\nline 9\nline 10\n"
|
||||
new := "line 1\nline 2\nline 3\nline 4\nchanged\nline 6\nline 7\nline 8\nline 9\nline 10\n"
|
||||
got := Diff("x", old, new)
|
||||
if strings.Contains(got, "line 1\n") || strings.Contains(got, "line 10\n") {
|
||||
t.Fatalf("diff contains distant unchanged content: %q", got)
|
||||
}
|
||||
if !strings.Contains(got, "-line 5\n+changed\n line 6\n") {
|
||||
t.Fatalf("diff lacks changed hunk: %q", got)
|
||||
}
|
||||
}
|
||||
func TestGlobAndGrep(t *testing.T) {
|
||||
d := t.TempDir()
|
||||
os.MkdirAll(filepath.Join(d, "sub"), 0755)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"9fans.net/go/plan9"
|
||||
|
|
@ -158,18 +159,19 @@ func (c *Conn) CallTool(ctx context.Context, name string, args json.RawMessage)
|
|||
defer fid.Close()
|
||||
|
||||
// Convert JSON args to key=value format
|
||||
var argMap map[string]interface{}
|
||||
var argMap map[string]any
|
||||
if err := json.Unmarshal(args, &argMap); err != nil {
|
||||
argMap = make(map[string]interface{})
|
||||
argMap = make(map[string]any)
|
||||
}
|
||||
|
||||
payload := fmt.Sprintf("token=%s\ntool=%s\nagent=%s\n", c.token, name, c.agentID)
|
||||
var payload strings.Builder
|
||||
fmt.Fprintf(&payload, "token=%s\ntool=%s\nagent=%s\n", c.token, name, c.agentID)
|
||||
for k, v := range argMap {
|
||||
escaped := escapeValue(fmt.Sprintf("%v", v))
|
||||
payload += fmt.Sprintf("%s=%s\n", k, escaped)
|
||||
fmt.Fprintf(&payload, "%s=%s\n", k, escaped)
|
||||
}
|
||||
|
||||
if _, err := fid.Write([]byte(payload)); err != nil {
|
||||
if _, err := fid.Write([]byte(payload.String())); err != nil {
|
||||
if ctx.Err() != nil {
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
|
|
@ -209,18 +211,19 @@ func (c *Conn) CallToolBackground(name string, args json.RawMessage) (int, error
|
|||
}
|
||||
defer fid.Close()
|
||||
|
||||
var argMap map[string]interface{}
|
||||
var argMap map[string]any
|
||||
if err := json.Unmarshal(args, &argMap); err != nil {
|
||||
argMap = make(map[string]interface{})
|
||||
argMap = make(map[string]any)
|
||||
}
|
||||
|
||||
payload := fmt.Sprintf("token=%s\ntool=%s\nagent=%s\n", c.token, name, c.agentID)
|
||||
var payload strings.Builder
|
||||
fmt.Fprintf(&payload, "token=%s\ntool=%s\nagent=%s\n", c.token, name, c.agentID)
|
||||
for k, v := range argMap {
|
||||
escaped := escapeValue(fmt.Sprintf("%v", v))
|
||||
payload += fmt.Sprintf("%s=%s\n", k, escaped)
|
||||
fmt.Fprintf(&payload, "%s=%s\n", k, escaped)
|
||||
}
|
||||
|
||||
if _, err := fid.Write([]byte(payload)); err != nil {
|
||||
if _, err := fid.Write([]byte(payload.String())); err != nil {
|
||||
return 0, fmt.Errorf("write: %w", err)
|
||||
}
|
||||
|
||||
|
|
@ -319,7 +322,7 @@ func (c *Conn) SetEnv(key, value string) error {
|
|||
return err
|
||||
}
|
||||
defer fid.Close()
|
||||
_, err = fid.Write([]byte(fmt.Sprintf("env %s=%s\n", key, value)))
|
||||
_, err = fmt.Fprintf(fid, "env %s=%s\n", key, value)
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -330,7 +333,7 @@ func (c *Conn) SetCWD(dir string) error {
|
|||
return err
|
||||
}
|
||||
defer fid.Close()
|
||||
_, err = fid.Write([]byte(fmt.Sprintf("cwd %s\n", dir)))
|
||||
_, err = fmt.Fprintf(fid, "cwd %s\n", dir)
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -447,7 +450,7 @@ func (c *Conn) SignalDetached(pid int, sig syscall.Signal) error {
|
|||
return err
|
||||
}
|
||||
defer fid.Close()
|
||||
_, err = fid.Write([]byte(fmt.Sprintf("signal %d\n", sig)))
|
||||
_, err = fmt.Fprintf(fid, "signal %d\n", sig)
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
// Package util manages the daemon-global environment for ollie.
|
||||
// It loads $XDG_CONFIG_HOME/ollie/env and provides defaults.
|
||||
package util
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"ollie/paths"
|
||||
)
|
||||
|
||||
// EnsureEnv loads $XDG_CONFIG_HOME/ollie/env and sets default values for any
|
||||
// OLLIE_* vars not already present in the process environment.
|
||||
func EnsureEnv() {
|
||||
f, err := os.Open(filepath.Join(paths.CfgDir(), "env"))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
sc := bufio.NewScanner(f)
|
||||
for sc.Scan() {
|
||||
line := strings.TrimSpace(sc.Text())
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
k, v, ok := strings.Cut(line, "=")
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
k = strings.TrimSpace(k)
|
||||
if i := strings.Index(v, " #"); i >= 0 {
|
||||
v = v[:i]
|
||||
}
|
||||
v = strings.TrimSpace(v)
|
||||
if len(v) >= 2 && (v[0] == '"' && v[len(v)-1] == '"' || v[0] == '\'' && v[len(v)-1] == '\'') {
|
||||
v = v[1 : len(v)-1]
|
||||
}
|
||||
if k != "" && os.Getenv(k) == "" {
|
||||
os.Setenv(k, paths.ExpandHome(os.ExpandEnv(v))) //nolint:errcheck
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -52,8 +52,8 @@ type FsNodeDecl struct {
|
|||
Stat func() os.FileInfo
|
||||
|
||||
// Twalk — children (implies directory; at most one set).
|
||||
Children []FsNodeDecl // static, known at compile time
|
||||
Bindings func() ([]FsNodeDecl, error) // Each: dynamic children
|
||||
Children []FsNodeDecl // static, known at compile time
|
||||
Bindings func() ([]FsNodeDecl, error) // Each: dynamic children
|
||||
|
||||
// Tread (non-blocking) + Twrite.
|
||||
Read func() ([]byte, error)
|
||||
|
|
|
|||
Loading…
Reference in New Issue