Files
bri-sandbox-development-pla…/agent-micro/internal/gitutil/gitutil.go
T
Achmad Setyabudi Susilo 3d99940658 Initial SDP skeleton
Sandbox Deployment Platform — Go control plane + agents, NextJS dashboard,
nginx reverse proxy. Cross-compile via Docker; deploy via sshpass to
172.18.136.92 (micro) and 172.18.139.186 (gateway).

- control-plane: HTTP API, WS hub, SQLite (modernc.org/sqlite) for
  progress, .log files for log persistence
- agent-micro / agent-gateway: alpine:3.20 + bind-mounted repo,
  binary exec'd in container, no Dockerfile build step
- dashboard: NextJS static export + shadcn/ui components, single
  WebSocket hook
- docker-compose.yml: three services on alpine:latest with docker
  socket bind for agents
- scripts/: build.sh (golang:1.23-alpine cross-compile), deploy.sh,
  patch-nginx.sh (idempotent nginx splice), ssh wrappers

Runtime model: pass-through Bitbucket creds per deploy, never logged or
persisted on the agent. Control plane never touches git or docker
directly — agents do all the work locally.
2026-06-24 07:25:01 +07:00

66 lines
2.3 KiB
Go

// Package gitutil wraps the few git operations the agent needs. Credentials
// are passed in per-call and never written to disk — every command sets them
// via -c credential helpers for the lifetime of the subprocess.
package gitutil
import (
"context"
"fmt"
"os/exec"
"strings"
)
// Creds is a username/password. We pass them through GIT_ASKPASS so they
// never appear on the command line or in process listings.
type Creds struct {
Username string
Password string
}
// Fetch runs `git fetch --prune origin`. Uses the per-command credential
// helper to inject creds without touching the repo's stored config.
func Fetch(ctx context.Context, repoDir string, c Creds) (string, error) {
return runGit(ctx, repoDir, c, "fetch", "--prune", "origin")
}
// Checkout switches to branch and updates the working tree.
func Checkout(ctx context.Context, repoDir, branch string, c Creds) (string, error) {
return runGit(ctx, repoDir, c, "checkout", "-f", branch)
}
// Pull fast-forwards the branch to match origin. Safe no-op if up to date.
func Pull(ctx context.Context, repoDir string, c Creds) (string, error) {
return runGit(ctx, repoDir, c, "pull", "--ff-only")
}
// Probe validates that the credentials work for a given remote. Used at
// login. Tries `git ls-remote <origin> HEAD`; succeeds even on an empty repo.
func Probe(ctx context.Context, repoDir string, c Creds) error {
_, err := runGit(ctx, repoDir, c, "ls-remote", "--heads", "origin", "HEAD")
return err
}
func runGit(ctx context.Context, repoDir string, c Creds, args ...string) (string, error) {
cmd := exec.CommandContext(ctx, "git", args...)
cmd.Dir = repoDir
// GIT_ASKPASS gives us a per-command credential helper. We just echo the
// creds back. The "username" / "password" args are sent to the script's
// argv by git.
askpass := fmt.Sprintf(`#!/bin/sh
case "$1" in
username) echo %q ;;
password) echo %q ;;
esac`, c.Username, c.Password)
cmd.Env = append(cmd.Environ(),
"GIT_ASKPASS=/dev/stdin",
"GIT_TERMINAL_PROMPT=0",
)
// ponytail: passing askpass via stdin is portable across Linux/macOS.
cmd.Stdin = strings.NewReader(askpass)
out, err := cmd.CombinedOutput()
if err != nil {
return string(out), fmt.Errorf("git %s: %w: %s", strings.Join(args, " "), err, out)
}
return string(out), nil
}