Set module to git.abrell.se/victor/mydev so it installs from the private Gitea host, and add an MIT LICENSE. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
158 lines
3.9 KiB
Go
158 lines
3.9 KiB
Go
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"sort"
|
|
"text/tabwriter"
|
|
|
|
"git.abrell.se/victor/mydev/internal/checks"
|
|
"git.abrell.se/victor/mydev/internal/config"
|
|
"git.abrell.se/victor/mydev/internal/run"
|
|
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
// devCompose loads config and returns the compose file path, exiting on error.
|
|
func devCompose() string {
|
|
cfg, err := config.Load()
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
file, err := cfg.ComposeFile()
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
return file
|
|
}
|
|
|
|
// compose runs `docker compose --file <file> <args...>` streamed to terminal.
|
|
func compose(file string, args ...string) {
|
|
full := append([]string{"compose", "--file", file}, args...)
|
|
if err := run.Stream("", "docker", full...); err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
// ensureAuthed re-auths first if the AWS session is gone (mirrors `myd auth`).
|
|
func ensureAuthed() {
|
|
cfg, err := config.Load()
|
|
if err != nil {
|
|
return
|
|
}
|
|
awsmfa, err := cfg.AWSMFA()
|
|
if err != nil {
|
|
return
|
|
}
|
|
if s := checks.AWSAuth(context.Background(), awsmfa); !s.Authed {
|
|
fmt.Println("AWS session expired — re-authenticating…")
|
|
_ = run.Stream("", awsmfa, "auth")
|
|
}
|
|
}
|
|
|
|
var devCmd = &cobra.Command{
|
|
Use: "dev",
|
|
Short: "Control the dev docker-compose stack",
|
|
}
|
|
|
|
var devStartCmd = &cobra.Command{
|
|
Use: "start",
|
|
Short: "Auth, then start the stack (up -d --force-recreate)",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
ensureAuthed()
|
|
compose(devCompose(), "up", "-d", "--force-recreate")
|
|
},
|
|
}
|
|
|
|
var devStopCmd = &cobra.Command{
|
|
Use: "stop",
|
|
Short: "Stop the stack",
|
|
Run: func(cmd *cobra.Command, args []string) { compose(devCompose(), "stop") },
|
|
}
|
|
|
|
var devRestartCmd = &cobra.Command{
|
|
Use: "restart",
|
|
Short: "Stop, auth, then start again",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
file := devCompose()
|
|
compose(file, "stop")
|
|
ensureAuthed()
|
|
compose(file, "up", "-d", "--force-recreate")
|
|
},
|
|
}
|
|
|
|
var devRemoveCmd = &cobra.Command{
|
|
Use: "remove",
|
|
Short: "Tear down the stack (down)",
|
|
Run: func(cmd *cobra.Command, args []string) { compose(devCompose(), "down") },
|
|
}
|
|
|
|
var devStatusFetch bool
|
|
|
|
var devStatusCmd = &cobra.Command{
|
|
Use: "status",
|
|
Short: "Check if running containers match the current dev repo",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
ctx := context.Background()
|
|
|
|
// Git freshness: is the dev repo itself behind origin?
|
|
cfg, err := config.Load()
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
if cfg.DevDir != "" {
|
|
if devStatusFetch {
|
|
fmt.Println("fetching dev repo…")
|
|
}
|
|
fr := checks.GitFreshness(ctx, cfg.DevDir, devStatusFetch)
|
|
switch {
|
|
case fr.Err != "":
|
|
fmt.Printf("dev repo: ? (%s)\n\n", fr.Err)
|
|
case fr.NoUpstream:
|
|
fmt.Printf("dev repo: %s (no upstream)\n\n", fr.Branch)
|
|
case fr.Behind > 0:
|
|
fmt.Printf("dev repo: %s ✗ behind origin by %d — git pull to get latest defs\n\n", fr.Branch, fr.Behind)
|
|
default:
|
|
fmt.Printf("dev repo: %s ✓ up to date with origin\n\n", fr.Branch)
|
|
}
|
|
}
|
|
|
|
file := devCompose()
|
|
drift, errMsg := checks.ComposeDrift(ctx, file)
|
|
if errMsg != "" {
|
|
fmt.Fprintln(os.Stderr, errMsg)
|
|
os.Exit(1)
|
|
}
|
|
sort.Slice(drift, func(i, j int) bool { return drift[i].Service < drift[j].Service })
|
|
|
|
stale := 0
|
|
w := tabwriter.NewWriter(os.Stdout, 0, 2, 2, ' ', 0)
|
|
for _, d := range drift {
|
|
mark := "✓"
|
|
if !d.UpToDate() {
|
|
mark = "✗"
|
|
stale++
|
|
}
|
|
fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", mark, d.Service, d.State, d.Reason())
|
|
}
|
|
w.Flush()
|
|
|
|
if stale > 0 {
|
|
fmt.Printf("\n%d service(s) out of date — run 'mydev dev restart' to recreate\n", stale)
|
|
os.Exit(1)
|
|
}
|
|
fmt.Println("\nall services up to date")
|
|
},
|
|
}
|
|
|
|
func init() {
|
|
devStatusCmd.Flags().BoolVar(&devStatusFetch, "fetch", true, "git fetch the dev repo first (set --fetch=false to skip network)")
|
|
devCmd.AddCommand(devStartCmd, devStopCmd, devRestartCmd, devRemoveCmd, devStatusCmd)
|
|
rootCmd.AddCommand(devCmd)
|
|
}
|