feat: agent deploy pipeline — passwordless SSH (alarm/desk/blanc-nas via tailnet IPs in ssh config), gitea write token to workers, secrets mounts, gitconfig, APPEND_SYSTEM guide

This commit is contained in:
Raphael Westphal
2026-08-20 09:42:58 +02:00
parent 6d10877e34
commit b35eca40c3
4 changed files with 48 additions and 22 deletions
+32 -14
View File
@@ -353,6 +353,36 @@ func (s *Spawner) cloneOrUpdate(ctx context.Context, repo, branch, slug string)
return nil
}
// giteaTokenEnv returns LVMH_GITEA_TOKEN=<pat> when a PAT is stored.
func giteaTokenEnv(store *Store) string {
if pat, ok, _ := store.GetSetting(settingGitLabToken); ok && pat != "" {
return "LVMH_GITEA_TOKEN=" + pat
}
return ""
}
// workerEnv assembles the env for a spawned worker container.
func workerEnv(s *Spawner, repo, sessionID string) []string {
env := []string{
envProviderAPIKey + "=" + os.Getenv(envProviderAPIKey),
envToken + "=" + daemonToken,
"LVMH_URL=" + s.containerURL,
envLVMHSessionID + "=" + sessionID,
envLVMHAgent + "=1",
// other provider keys (empty ones are harmless)
"OPENAI_API_KEY=" + os.Getenv("OPENAI_API_KEY"),
"GEMINI_API_KEY=" + os.Getenv("GEMINI_API_KEY"),
"DEEPSEEK_KEY=" + os.Getenv("DEEPSEEK_KEY"),
"ANTHROPIC_API_KEY=" + os.Getenv("ANTHROPIC_API_KEY"),
envLVMHRepo + "=" + repo,
}
// Gitea token (write scope) so agents can push and open PRs.
if e := giteaTokenEnv(s.store); e != "" {
env = append(env, e)
}
return env
}
// cloneURL builds the clean https clone URL (never credential-bearing).
func (s *Spawner) cloneURL(repo string) (string, error) {
u, err := url.Parse(s.baseURL + "/" + repo + ".git")
@@ -536,20 +566,8 @@ func (s *Spawner) createAndStart(ctx context.Context, repo, slug, sessionID stri
)
}
cfg := &container.Config{
Image: image,
Env: []string{
envProviderAPIKey + "=" + os.Getenv(envProviderAPIKey),
envToken + "=" + daemonToken,
"LVMH_URL=" + s.containerURL,
envLVMHSessionID + "=" + sessionID,
envLVMHAgent + "=1",
// other provider keys (empty ones are harmless)
"OPENAI_API_KEY=" + os.Getenv("OPENAI_API_KEY"),
"GEMINI_API_KEY=" + os.Getenv("GEMINI_API_KEY"),
"DEEPSEEK_KEY=" + os.Getenv("DEEPSEEK_KEY"),
"ANTHROPIC_API_KEY=" + os.Getenv("ANTHROPIC_API_KEY"),
envLVMHRepo + "=" + repo,
},
Image: image,
Env: workerEnv(s, repo, sessionID),
Labels: map[string]string{labelSession: sessionID},
}
hostCfg := &container.HostConfig{
+1
View File
@@ -72,6 +72,7 @@ func TestSpawnerStartHappyPath(t *testing.T) {
passthrough := map[string]bool{
"OPENAI_API_KEY": true, "GEMINI_API_KEY": true,
"DEEPSEEK_KEY": true, "ANTHROPIC_API_KEY": true,
"LVMH_GITEA_TOKEN": true,
}
for _, e := range c.Env {
if name, _, ok := strings.Cut(e, "="); ok && passthrough[name] {
+9
View File
@@ -1,3 +1,12 @@
Host alarm
HostName 100.64.84.69
User alarm
Host desk
HostName 100.73.57.3
User raph
Host blanc-nas
HostName 100.91.19.107
User raph
Host alarm desk blanc-nas
IdentityFile /root/.ssh/id_ed25519
IdentitiesOnly yes
+6 -8
View File
@@ -1,8 +1,6 @@
# blanc-nas:22 SSH-2.0-OpenSSH_10.4
blanc-nas ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC7CmHNP4LxohSCJY1acgMqPPb0hvjlbDzzvf1oqPLcaGs+KsRL2+OfMbNHOMBz4XfM5WN5DANOCuOBJBtn1vyAu83JXLx0XTl6pEnmaBm6Wjs05zmI1fliGJHgbPsPCz8Y+kRNY1Z01VWWsanr7Yu9LStcyKY1v2i2DqQ9i3WdTzWyE9Tkt3540/49LFVjkRR6lGeh2G0/ppM3utM2Rg/CGMjbmYV9LBtU5CTtrEWiGLyDwxuXNyk6BcQkxOHZf76W05/FwvHRv15mwF0RxkDitmfzWMEQ/+paUcDMRgLKF1j2mBtR2UdR3z7m4q46PiXKAjyJAG7tIfJqwr1jSRYXn6+bEV5tK6VJkc2+0viMoQYrnBBxL676rShECG27Ib8loUk2PuhXUSVWiLY+/BvRyXETIZxgoF53LCO5Z0Wh91Dp2OMfO0EajfRc83FfqOFgwhGpVk27r+LcLjYzqz5BUIy58HtSpZEQRr1KJuagZFfnLDRtY9ovn3uBDQhpX0k=
# blanc-nas:22 SSH-2.0-OpenSSH_10.4
blanc-nas ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFB7p37Rxw6tW2rUpoECwtXLBnU179CxiwTNtwJQs7Sp
# alarm:22 SSH-2.0-OpenSSH_10.4
alarm ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCwjQpOAwCL0AIGeJzI99CoOFTxUFUd2+DCveZKeBfCpdyZAtrPTd5310VjyAH0HWXd3c4fZvgPcrjSzzbygpSroZD9zQh6zadp+yCXZNoLrc2iT8CkSngw24QpvOfdkunJiwnRjKLflYrCvzu0BHAoDKHq8XjpTpwfN0BH/eoIIqcoDnmF9KrTUQRbEzwg+xc1T41ByjIalqHfrdNxV0S8xv+sQ7/YvGmi4YVZJ5ElqEEgGtyBDJE3WuveH5sA+YkdoMMEm9Xf+xlgO6uwhaYAKbG29Rkq9Ot8N7NSf3mu00XVK5wHvHLYqFBhA24Pw5fuDp0ohbhHKIbpumS5GP8Ow3eR/bLIesSGkaPnB4o6t8HiQ3TKfdCO8UAvH85j6gXDiPmFXybOmxQMB+oWo+zdOYa8XVryHB3CJ+4yBmJU03dzNwpV+93uVoW9jmjElRwZx9c+gqSBUWgWlaVF1AFdVT2swzKIBTJg2ZXjoqLphuWfnewhPKPt48Fr5B0OAYk=
# alarm:22 SSH-2.0-OpenSSH_10.4
alarm ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGIoBFYAfkN1Q8XsjwsJZ2mLIQi9XQ9M03zEENW8Injq
# 100.64.84.69:22 SSH-2.0-OpenSSH_10.4
100.64.84.69 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGIoBFYAfkN1Q8XsjwsJZ2mLIQi9XQ9M03zEENW8Injq
# 100.73.57.3:22 SSH-2.0-OpenSSH_10.4
100.73.57.3 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIN5kwgNZ/IYLpAxVtezTxYLcy7MmP9lKnrKX0XeBiCIn
# 100.91.19.107:22 SSH-2.0-OpenSSH_10.4
100.91.19.107 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFB7p37Rxw6tW2rUpoECwtXLBnU179CxiwTNtwJQs7Sp