Skip to content

Commit 013ccb1

Browse files
AndroidPoetclaude
andcommitted
Add 11 new command groups and expand to 80+ subcommands
New commands: - completion: shell completions for bash/zsh/fish/powershell - doctor: validate CLI setup and credentials (6 checks) - init: create .gpc.yaml project config with auto-discovery - offers: manage subscription offers (list/get/create/update/delete/activate/deactivate) - deobfuscation: upload ProGuard mapping files and native debug symbols - orders: view order details, issue refunds, batch-get - external-transactions: alternative billing compliance (create/get/refund) - recovery: app recovery actions for production incidents - device-tiers: device tier configurations for targeted delivery - availability: country targeting per release track - diff: compare draft edit state against live version Enhancements: - Add CSV and YAML output formats (--output csv/yaml) - Expand vitals with 7 new metrics: slow-start, slow-rendering, wakeups, wakelocks, memory, errors, error issues - Add .gpc.yaml project config loading in root command - Add 5 new API client accessors (Orders, ExternalTransactions, AppRecovery, GeneratedAPKs, SystemAPKs) - Comprehensive command reference at docs/commands.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent b409084 commit 013ccb1

21 files changed

Lines changed: 2994 additions & 12 deletions

File tree

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,9 @@ Thumbs.db
3232

3333
# Config (local)
3434
.gpc/
35+
.gpc.yaml
36+
37+
# AI tools
38+
.claude/
39+
.tldr/
40+
.tldrignore

README.md

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ gpc tracks promote --from internal --to production --rollout 10
8585

8686
## 🎯 Commands
8787

88+
**30 command groups, 80+ subcommands.** [Full reference →](docs/commands.md)
89+
8890
### 📤 Release Management
8991

9092
```bash
@@ -93,6 +95,7 @@ gpc tracks list # List tracks
9395
gpc tracks promote --from internal --to beta # Promote
9496
gpc tracks update --track production --rollout 50 # Staged rollout
9597
gpc tracks halt --track production # Emergency halt
98+
gpc deobfuscation upload --version-code 42 --file mapping.txt # Crash symbolication
9699
```
97100

98101
### 🏪 Store Presence
@@ -101,6 +104,7 @@ gpc tracks halt --track production # Emergency halt
101104
gpc listings sync --dir ./metadata/ # Sync all listings
102105
gpc listings update --locale en-US --title "My App" # Update listing
103106
gpc images sync --dir ./screenshots/ # Sync screenshots
107+
gpc availability list --track production # Country targeting
104108
```
105109

106110
### ⭐ Reviews
@@ -116,22 +120,33 @@ gpc reviews list | jq '[.[] | select(.rating == 5)]' # Filter with jq
116120
```bash
117121
gpc products list # In-app products
118122
gpc subscriptions list # Subscriptions
123+
gpc offers list --product-id sub_id --base-plan monthly # Subscription offers
119124
gpc purchases verify --token "..." --product-id premium
125+
gpc orders get --order-id GPA.1234 # Order details
126+
gpc orders refund --order-id GPA.1234 --confirm # Issue refund
127+
gpc external-transactions create --file tx.json # Alternative billing
120128
```
121129

122130
### 📊 Analytics & Vitals
123131

124132
```bash
125133
gpc vitals overview # Health summary
126-
gpc vitals crashes --days 7 # Crash metrics
127-
gpc vitals anr --days 30 # ANR metrics
134+
gpc vitals crashes --days 7 # Crash rate
135+
gpc vitals anr --days 28 # ANR rate
136+
gpc vitals slow-start --days 28 # Slow startup rate
137+
gpc vitals slow-rendering --days 28 # Frame rendering
138+
gpc vitals wakeups --days 28 # Battery: wakeup alarms
139+
gpc vitals wakelocks --days 28 # Battery: stuck wakelocks
140+
gpc vitals memory --days 28 # Low memory killer rate
141+
gpc vitals errors issues # Grouped error issues
128142
```
129143

130144
### 📱 Devices
131145

132146
```bash
133147
gpc devices list # Supported devices
134148
gpc devices stats # Device distribution
149+
gpc device-tiers list # Device tier configs
135150
```
136151

137152
### 📈 Reports
@@ -155,6 +170,16 @@ gpc users list
155170
gpc users grant --email "dev@company.com" --role releaseManager
156171
```
157172

173+
### 🛠️ Utilities
174+
175+
```bash
176+
gpc doctor # Validate setup
177+
gpc init --package com.example.app # Create project config
178+
gpc diff # Compare draft vs live
179+
gpc recovery list # App recovery actions
180+
gpc completion zsh > "${fpath[1]}/_gpc" # Shell completions
181+
```
182+
158183
---
159184

160185
## 🔄 CI/CD Integration
@@ -209,7 +234,7 @@ base64 < service-account.json | xclip # Linux
209234
| `GPC_CREDENTIALS_B64` | Base64-encoded credentials (CI) |
210235
| `GPC_PACKAGE` | Default package name |
211236
| `GPC_PROFILE` | Auth profile to use |
212-
| `GPC_OUTPUT` | Format: `json` \| `table` \| `tsv` |
237+
| `GPC_OUTPUT` | Format: `json` \| `table` \| `tsv` \| `csv` \| `yaml` |
213238

214239
---
215240

@@ -218,8 +243,11 @@ base64 < service-account.json | xclip # Linux
218243
```bash
219244
gpc tracks list # JSON (default, for scripting)
220245
gpc tracks list --pretty # Pretty JSON
221-
gpc tracks list --output table # ASCII table
222-
gpc tracks list --output tsv # Tab-separated
246+
gpc tracks list -o table # ASCII table
247+
gpc tracks list -o tsv # Tab-separated values
248+
gpc tracks list -o csv # Comma-separated values
249+
gpc tracks list -o yaml # YAML
250+
gpc tracks list -o minimal # First field only (piping)
223251
```
224252

225253
---
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
package availability
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
"time"
7+
8+
"github.com/spf13/cobra"
9+
"google.golang.org/api/androidpublisher/v3"
10+
11+
"github.com/AndroidPoet/playconsole-cli/internal/api"
12+
"github.com/AndroidPoet/playconsole-cli/internal/cli"
13+
"github.com/AndroidPoet/playconsole-cli/internal/output"
14+
)
15+
16+
// AvailabilityCmd manages country targeting for releases
17+
var AvailabilityCmd = &cobra.Command{
18+
Use: "availability",
19+
Short: "Manage country availability for releases",
20+
Long: `View and update which countries your app is available in.
21+
22+
Country targeting is configured per release track. You can
23+
restrict availability to specific countries or include all
24+
countries with specific exclusions.`,
25+
}
26+
27+
var listCmd = &cobra.Command{
28+
Use: "list",
29+
Short: "List country targeting for a track",
30+
RunE: runList,
31+
}
32+
33+
var updateCmd = &cobra.Command{
34+
Use: "update",
35+
Short: "Update country targeting for a track",
36+
RunE: runUpdate,
37+
}
38+
39+
var (
40+
track string
41+
countries string
42+
includeRest bool
43+
)
44+
45+
func init() {
46+
listCmd.Flags().StringVar(&track, "track", "production", "release track")
47+
48+
updateCmd.Flags().StringVar(&track, "track", "production", "release track")
49+
updateCmd.Flags().StringVar(&countries, "countries", "", "comma-separated country codes (e.g., US,GB,DE)")
50+
updateCmd.Flags().BoolVar(&includeRest, "include-rest", true, "include rest of world")
51+
updateCmd.Flags().Bool("confirm", false, "confirm destructive operation")
52+
updateCmd.MarkFlagRequired("countries")
53+
54+
AvailabilityCmd.AddCommand(listCmd)
55+
AvailabilityCmd.AddCommand(updateCmd)
56+
}
57+
58+
// CountryInfo represents country targeting information
59+
type CountryInfo struct {
60+
Track string `json:"track"`
61+
Countries []string `json:"countries,omitempty"`
62+
IncludeRest bool `json:"include_rest_of_world"`
63+
ReleaseVersion string `json:"release_version,omitempty"`
64+
}
65+
66+
func runList(cmd *cobra.Command, args []string) error {
67+
if err := cli.RequirePackage(cmd); err != nil {
68+
return err
69+
}
70+
71+
client, err := api.NewClient(cli.GetPackageName(), 60*time.Second)
72+
if err != nil {
73+
return err
74+
}
75+
76+
// Create edit to read track info
77+
edit, err := client.CreateEdit()
78+
if err != nil {
79+
return err
80+
}
81+
defer edit.Close()
82+
defer edit.Delete()
83+
84+
trackResp, err := edit.Tracks().Get(
85+
client.GetPackageName(), edit.ID(), track,
86+
).Context(edit.Context()).Do()
87+
if err != nil {
88+
return fmt.Errorf("failed to get track '%s': %w", track, err)
89+
}
90+
91+
info := CountryInfo{
92+
Track: track,
93+
}
94+
95+
if len(trackResp.Releases) > 0 {
96+
latest := trackResp.Releases[0]
97+
if latest.CountryTargeting != nil {
98+
info.Countries = latest.CountryTargeting.Countries
99+
info.IncludeRest = latest.CountryTargeting.IncludeRestOfWorld
100+
}
101+
if len(latest.VersionCodes) > 0 {
102+
info.ReleaseVersion = fmt.Sprintf("%d", latest.VersionCodes[0])
103+
}
104+
if latest.Name != "" {
105+
info.ReleaseVersion = latest.Name
106+
}
107+
}
108+
109+
return output.Print(info)
110+
}
111+
112+
func runUpdate(cmd *cobra.Command, args []string) error {
113+
if err := cli.RequirePackage(cmd); err != nil {
114+
return err
115+
}
116+
117+
if err := cli.CheckConfirm(cmd); err != nil {
118+
return err
119+
}
120+
121+
countryCodes := strings.Split(countries, ",")
122+
for i := range countryCodes {
123+
countryCodes[i] = strings.TrimSpace(strings.ToUpper(countryCodes[i]))
124+
}
125+
126+
if cli.IsDryRun() {
127+
output.PrintInfo("Dry run: would update track '%s' with countries: %s (include rest: %v)",
128+
track, strings.Join(countryCodes, ", "), includeRest)
129+
return nil
130+
}
131+
132+
client, err := api.NewClient(cli.GetPackageName(), 60*time.Second)
133+
if err != nil {
134+
return err
135+
}
136+
137+
// Create edit
138+
edit, err := client.CreateEdit()
139+
if err != nil {
140+
return err
141+
}
142+
defer edit.Close()
143+
144+
// Get current track
145+
trackResp, err := edit.Tracks().Get(
146+
client.GetPackageName(), edit.ID(), track,
147+
).Context(edit.Context()).Do()
148+
if err != nil {
149+
return fmt.Errorf("failed to get track '%s': %w", track, err)
150+
}
151+
152+
if len(trackResp.Releases) == 0 {
153+
return fmt.Errorf("no releases found on track '%s'", track)
154+
}
155+
156+
// Update country targeting on latest release
157+
for _, release := range trackResp.Releases {
158+
release.CountryTargeting = &androidpublisher.CountryTargeting{
159+
Countries: countryCodes,
160+
IncludeRestOfWorld: includeRest,
161+
}
162+
}
163+
164+
// Update track
165+
_, err = edit.Tracks().Update(
166+
client.GetPackageName(), edit.ID(), track, trackResp,
167+
).Context(edit.Context()).Do()
168+
if err != nil {
169+
return fmt.Errorf("failed to update track: %w", err)
170+
}
171+
172+
// Commit
173+
if err := edit.Commit(); err != nil {
174+
return err
175+
}
176+
177+
output.PrintSuccess("Country targeting updated for track '%s'", track)
178+
return nil
179+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package completion
2+
3+
import (
4+
"os"
5+
6+
"github.com/spf13/cobra"
7+
)
8+
9+
// CompletionCmd generates shell completion scripts
10+
var CompletionCmd = &cobra.Command{
11+
Use: "completion [bash|zsh|fish|powershell]",
12+
Short: "Generate shell completion scripts",
13+
Long: `Generate shell completion scripts for playconsole-cli.
14+
15+
To load completions:
16+
17+
Bash:
18+
$ source <(gpc completion bash)
19+
20+
# To load for each session (Linux):
21+
$ gpc completion bash > /etc/bash_completion.d/gpc
22+
23+
# To load for each session (macOS):
24+
$ gpc completion bash > $(brew --prefix)/etc/bash_completion.d/gpc
25+
26+
Zsh:
27+
# If shell completion is not already enabled, enable it:
28+
$ echo "autoload -U compinit; compinit" >> ~/.zshrc
29+
30+
$ source <(gpc completion zsh)
31+
32+
# To load for each session:
33+
$ gpc completion zsh > "${fpath[1]}/_gpc"
34+
35+
Fish:
36+
$ gpc completion fish | source
37+
38+
# To load for each session:
39+
$ gpc completion fish > ~/.config/fish/completions/gpc.fish
40+
41+
PowerShell:
42+
PS> gpc completion powershell | Out-String | Invoke-Expression
43+
44+
# To load for each session:
45+
PS> gpc completion powershell > gpc.ps1
46+
# and source this file from your PowerShell profile.
47+
`,
48+
DisableFlagsInUseLine: true,
49+
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
50+
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
51+
RunE: func(cmd *cobra.Command, args []string) error {
52+
switch args[0] {
53+
case "bash":
54+
return cmd.Root().GenBashCompletion(os.Stdout)
55+
case "zsh":
56+
return cmd.Root().GenZshCompletion(os.Stdout)
57+
case "fish":
58+
return cmd.Root().GenFishCompletion(os.Stdout, true)
59+
case "powershell":
60+
return cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
61+
}
62+
return nil
63+
},
64+
}

0 commit comments

Comments
 (0)