@@ -3,92 +3,115 @@ package cmd
33import (
44 "context"
55 "fmt"
6- "os/exec "
6+ "os"
77 "strings"
88
99 "github.com/javaBin/javabin-cli/internal/aws"
10- "github.com/javaBin/javabin-cli/internal/config"
1110 "github.com/spf13/cobra"
11+ "gopkg.in/yaml.v3"
1212)
1313
14- var projectFlag string
14+ var teamFlag string
15+ var serviceFlag string
1516
1617var statusCmd = & cobra.Command {
1718 Use : "status" ,
18- Short : "Show project status (costs, services, deployments)" ,
19- RunE : runStatus ,
19+ Short : "Show team costs and ECS service status" ,
20+ Long : `Show month-to-date AWS costs for a team and ECS service status.
21+
22+ Flags --team and --service override auto-detection. If run from a directory
23+ with an app.yaml, team and service name are read from it automatically.` ,
24+ RunE : runStatus ,
2025}
2126
2227func init () {
23- statusCmd .Flags ().StringVar (& projectFlag , "project" , "" , "Project name (inferred from git remote if not set)" )
28+ statusCmd .Flags ().StringVar (& teamFlag , "team" , "" , "Team name (reads from app.yaml if not set)" )
29+ statusCmd .Flags ().StringVar (& serviceFlag , "service" , "" , "Service name (reads from app.yaml if not set)" )
30+ }
31+
32+ type appYaml struct {
33+ Name string `yaml:"name"`
34+ Team string `yaml:"team"`
35+ }
36+
37+ func readAppYaml () * appYaml {
38+ data , err := os .ReadFile ("app.yaml" )
39+ if err != nil {
40+ return nil
41+ }
42+ var app appYaml
43+ if err := yaml .Unmarshal (data , & app ); err != nil {
44+ return nil
45+ }
46+ return & app
2447}
2548
2649func runStatus (cmd * cobra.Command , args []string ) error {
27- project := projectFlag
28- if project == "" {
29- project = inferProject ()
50+ team := teamFlag
51+ service := serviceFlag
52+
53+ if team == "" || service == "" {
54+ if app := readAppYaml (); app != nil {
55+ if team == "" {
56+ team = app .Team
57+ }
58+ if service == "" {
59+ service = app .Name
60+ }
61+ }
62+ }
63+
64+ if team == "" {
65+ return fmt .Errorf ("could not determine team — use --team flag or run from a directory with app.yaml" )
3066 }
31- if project == "" {
32- return fmt .Errorf ("could not infer project name — use --project flag or run from a javaBin repo" )
67+
68+ fmt .Printf ("Team: %s\n " , team )
69+ if service != "" {
70+ fmt .Printf ("Service: %s\n " , service )
3371 }
72+ fmt .Println ()
3473
35- fmt .Printf ("Project: %s\n \n " , project )
3674 ctx := context .Background ()
37-
3875 cfg , err := aws .LoadConfig (ctx )
3976 if err != nil {
4077 return fmt .Errorf ("AWS credentials not configured: %w" , err )
4178 }
4279
4380 // Cost this month
4481 fmt .Println ("--- Costs (month-to-date) ---" )
45- cost , err := aws .GetMonthlyCost (ctx , cfg , project )
82+ cost , err := aws .GetTeamMonthlyCost (ctx , cfg , team )
4683 if err != nil {
4784 fmt .Printf (" Could not fetch costs: %v\n " , err )
4885 } else {
49- fmt .Printf (" Spend : $%.2f\n " , cost )
86+ fmt .Printf (" Team spend : $%.2f\n " , cost )
5087 }
5188
5289 // ECS services
5390 fmt .Println ("\n --- ECS Services ---" )
5491 services , err := aws .ListServices (ctx , cfg , "javabin-platform" )
5592 if err != nil {
5693 fmt .Printf (" Could not list services: %v\n " , err )
57- } else if len (services ) == 0 {
58- fmt .Println (" No running services" )
5994 } else {
95+ prefix := team + "-"
96+ found := false
6097 for _ , svc := range services {
61- if strings .Contains (svc .Name , project ) {
62- fmt .Printf (" %s running=%d desired=%d\n " , svc .Name , svc .RunningCount , svc .DesiredCount )
98+ if ! strings .HasPrefix (svc .Name , prefix ) {
99+ continue
100+ }
101+ if service != "" && svc .Name != prefix + service {
102+ continue
103+ }
104+ fmt .Printf (" %s running=%d desired=%d\n " , svc .Name , svc .RunningCount , svc .DesiredCount )
105+ found = true
106+ }
107+ if ! found {
108+ if service != "" {
109+ fmt .Printf (" No services matching %s%s\n " , prefix , service )
110+ } else {
111+ fmt .Printf (" No services matching %s*\n " , prefix )
63112 }
64113 }
65114 }
66115
67- // TODO: Last 5 deployments (requires ECS describe-services with deployments)
68- // TODO: Untagged resources (requires Config or resource group tagging API)
69-
70- _ = config .EnsureConfigDir ()
71116 return nil
72117}
73-
74- func inferProject () string {
75- out , err := exec .Command ("git" , "remote" , "get-url" , "origin" ).Output ()
76- if err != nil {
77- return ""
78- }
79- url := strings .TrimSpace (string (out ))
80- // Handle both HTTPS and SSH URLs
81- // https://github.com/javaBin/moresleep.git -> moresleep
82- // git@github.com:javaBin/moresleep.git -> moresleep
83- for _ , prefix := range []string {
84- "https://github.com/javaBin/" ,
85- "git@github.com:javaBin/" ,
86- } {
87- if strings .HasPrefix (url , prefix ) {
88- name := strings .TrimPrefix (url , prefix )
89- name = strings .TrimSuffix (name , ".git" )
90- return name
91- }
92- }
93- return ""
94- }
0 commit comments