Files
kandev/apps/backend/internal/agent/lifecycle/credential_uploader.go
T
José Almeida 948743600e refactor(cleanup): drop dead code, dedupe predicates, unify token injection
Tier 1 of the post-review cleanup. Each item is independently safe; bundling
because they're all small, mechanical, and lint-passing.

- fix(clone): unify injectTokenIntoURL across Docker and Sprites — Sprites
  used to ignore GH_TOKEN entirely (a user with only GH_TOKEN set got 401
  on Sprites clone but worked on Docker). Promote one
  `injectGitHubTokenIntoCloneURL` helper, accept both env vars
  (GITHUB_TOKEN wins, matching Docker), and use the documented
  `https://x-access-token:T@github.com/` form on both surfaces.
- fix(executor-popover): swap the <span tabIndex role="button"> trigger
  for <Button variant="ghost"> so Enter/Space activation, focus rings on
  Safari, and disabled semantics come for free.
- test(docker): rewrite TestDockerStopInstance_PreservesSessionDirOnPlainStop
  to drive the real DockerExecutor.StopInstance path with ContainerID=""
  (skips the docker daemon, exercises the kandev-side cleanup branch).
  Previous version only iterated shouldRunExecutorCleanup strings — would
  pass even if StopInstance always cleaned up. Now table-driven over
  preserve+destructive reasons with on-disk asserts.
- refactor(orchestrator): drop dead `_ = arg` parameters from persist*
  helpers (persistResumeState, persistModelSwitchState, persistLaunchState).
  Brings signatures from 9→6 args, under the funlen lint cap.
- refactor(orchestrator): collapse byte-identical isContainerizedExecutor
  ≡ isCloneBasedExecutorType predicate into one.
- refactor(lifecycle): drop dead getMetadataBool, FileUploader.RunCommand,
  expandMountSource {home} arm (no production agent ships {home} in
  Mounts; codex_acp_test asserts it), dockerTaskBranch one-line wrapper.
2026-05-07 00:29:12 +01:00

85 lines
2.2 KiB
Go

package lifecycle
import (
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
"go.uber.org/zap"
"github.com/kandev/kandev/internal/agent/remoteauth"
"github.com/kandev/kandev/internal/common/logger"
)
// FileUploader abstracts writing files to a remote environment. Used by
// UploadCredentialFiles to seed agent auth files into the kandev-managed
// per-container session dir (local) or sprite (remote).
type FileUploader interface {
WriteFile(ctx context.Context, path string, data []byte, mode os.FileMode) error
}
// UploadCredentialFiles reads local credential files and uploads them to the remote environment.
func UploadCredentialFiles(
ctx context.Context,
uploader FileUploader,
methods []remoteauth.Method,
targetHomeDir string,
log *logger.Logger,
) error {
if len(methods) == 0 {
return nil
}
home, err := os.UserHomeDir()
if err != nil {
return fmt.Errorf("failed to get home directory: %w", err)
}
for _, method := range methods {
if method.Type != "files" {
continue
}
for _, relPath := range method.SourceFiles {
srcPath := filepath.Join(home, relPath)
data, readErr := os.ReadFile(srcPath)
if readErr != nil {
log.Warn("credential source file not found, skipping",
zap.String("method_id", method.MethodID),
zap.String("path", srcPath))
continue
}
targetPath := filepath.Join(targetHomeDir, method.TargetRelDir, filepath.Base(relPath))
if err := uploader.WriteFile(ctx, targetPath, data, 0o644); err != nil {
return fmt.Errorf("failed to upload %s: %w", targetPath, err)
}
log.Debug("uploaded credential file",
zap.String("method_id", method.MethodID),
zap.String("target", targetPath))
}
}
return nil
}
// DetectGHToken runs `gh auth token` locally and returns the GitHub OAuth token.
func DetectGHToken() (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
cmd := exec.CommandContext(ctx, "gh", "auth", "token")
out, err := cmd.Output()
if err != nil {
return "", fmt.Errorf("gh auth token failed: %w", err)
}
token := strings.TrimSpace(string(out))
if token == "" {
return "", fmt.Errorf("gh auth token returned empty")
}
return token, nil
}