3d99940658
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.
66 lines
2.3 KiB
Go
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
|
|
}
|