Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
18f4c44
transitioning to explicit argument passing (#284)
digitalghost-dev May 7, 2026
05b45dd
transitioning to explicit argument passing (#284)
digitalghost-dev May 7, 2026
0dd6b49
transitioning to explicit argument passing (#284)
digitalghost-dev May 7, 2026
45ace9c
transitioning to explicit argument passing (#284)
digitalghost-dev May 7, 2026
8766c52
transitioning to explicit argument passing (#284)
digitalghost-dev May 7, 2026
2d742f3
transitioning to explicit argument passing (#284)
digitalghost-dev May 7, 2026
c0bd2d0
transitioning to explicit argument passing (#284)
digitalghost-dev May 7, 2026
59ad37f
transitioning to explicit argument passing (#284)
digitalghost-dev May 7, 2026
c4ab69d
transitioning to explicit argument passing (#284)
digitalghost-dev May 7, 2026
878b9fb
transitioning to explicit argument passing (#284)
digitalghost-dev May 7, 2026
285ebeb
transitioning to explicit argument passing (#284)
digitalghost-dev May 7, 2026
5417a7b
transitioning to explicit argument passing (#284)
digitalghost-dev May 7, 2026
031e68f
transitioning to explicit argument passing (#284)
digitalghost-dev May 7, 2026
f92013e
updating version numbers
digitalghost-dev May 7, 2026
41f7ac8
removing binary name call, only `args` now (#284)
digitalghost-dev May 7, 2026
0acef1d
updating argument positions
digitalghost-dev May 7, 2026
4de8f30
updating docs
digitalghost-dev May 7, 2026
0cd904d
hardening API and HTTP request handling (#285)
digitalghost-dev May 7, 2026
a0d742e
centralizing `http` timeout variable (#286)
digitalghost-dev May 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ on:
- main

env:
VERSION_NUMBER: 'v1.10.1'
VERSION_NUMBER: 'v1.10.2'
DOCKERHUB_REGISTRY_NAME: 'digitalghostdev/poke-cli'
AWS_REGION: 'us-west-2'

Expand Down
2 changes: 1 addition & 1 deletion .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ builds:
- windows
- darwin
ldflags:
- -s -w -X main.version=v1.10.1
- -s -w -X main.version=v1.10.2

archives:
- formats: [ 'zip' ]
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ RUN go mod download

COPY . .

RUN go build -ldflags "-X main.version=v1.10.1" -o poke-cli .
RUN go build -ldflags "-X main.version=v1.10.2" -o poke-cli .

# build 2
FROM --platform=$BUILDPLATFORM alpine:3.23
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<img width="425" src="poke-cli.png" alt="pokemon-logo"/>
<h4></h4>
<img src="https://img.shields.io/github/v/release/digitalghost-dev/poke-cli?style=flat-square&logo=git&logoColor=FFCC00&label=Release%20Version&labelColor=EEE&color=FFCC00" alt="version-label">
<img src="https://img.shields.io/docker/image-size/digitalghostdev/poke-cli/v1.10.1?arch=arm64&style=flat-square&logo=docker&logoColor=FFCC00&labelColor=EEE&color=FFCC00" alt="docker-image-size">
<img src="https://img.shields.io/docker/image-size/digitalghostdev/poke-cli/v1.10.2?arch=arm64&style=flat-square&logo=docker&logoColor=FFCC00&labelColor=EEE&color=FFCC00" alt="docker-image-size">
<img src="https://img.shields.io/github/actions/workflow/status/digitalghost-dev/poke-cli/ci.yml?branch=main&style=flat-square&logo=github&logoColor=FFCC00&label=CI&labelColor=EEE&color=FFCC00" alt="ci-status-badge">
</div>
<div align="center">
Expand Down Expand Up @@ -99,11 +99,11 @@ Cloudsmith is a fully cloud-based service that lets you easily create, store, an
3. Choose how to interact with the container:
* Run a single command and exit:
```bash
docker run --rm -it digitalghostdev/poke-cli:v1.10.1 <command> [subcommand] [flag]
docker run --rm -it digitalghostdev/poke-cli:v1.10.2 <command> [subcommand] [flag]
```
* Enter the container and use its shell:
```bash
docker run --rm -it --name poke-cli --entrypoint /bin/sh digitalghostdev/poke-cli:v1.10.1 -c "cd /app && exec sh"
docker run --rm -it --name poke-cli --entrypoint /bin/sh digitalghostdev/poke-cli:v1.10.2 -c "cd /app && exec sh"
# placed into the /app directory, run the program with './poke-cli'
# example: ./poke-cli ability swift-swim
```
Expand All @@ -112,13 +112,13 @@ Cloudsmith is a fully cloud-based service that lets you easily create, store, an
> The `card` command renders TCG card images using your terminal's graphics protocol. When running inside Docker, pass your terminal's environment variables so image rendering works correctly:
> ```bash
> # Kitty
> docker run --rm -it -e TERM -e KITTY_WINDOW_ID digitalghostdev/poke-cli:v1.10.1 card
> docker run --rm -it -e TERM -e KITTY_WINDOW_ID digitalghostdev/poke-cli:v1.10.2 card
>
> # WezTerm, iTerm2, Ghostty, Konsole, Rio, Tabby
> docker run --rm -it -e TERM -e TERM_PROGRAM digitalghostdev/poke-cli:v1.10.1 card
> docker run --rm -it -e TERM -e TERM_PROGRAM digitalghostdev/poke-cli:v1.10.2 card
>
> # Windows Terminal (Sixel)
> docker run --rm -it -e WT_SESSION digitalghostdev/poke-cli:v1.10.1 card
> docker run --rm -it -e WT_SESSION digitalghostdev/poke-cli:v1.10.2 card
> ```
> If your terminal is not listed above, image rendering is not supported inside Docker.

Expand Down
2 changes: 1 addition & 1 deletion card_data/pipelines/poke_cli_dbt/dbt_project.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: 'poke_cli_dbt'
version: '1.10.1'
version: '1.10.2'

profile: 'poke_cli_dbt'

Expand Down
2 changes: 1 addition & 1 deletion card_data/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "card-data"
version = "v1.10.1"
version = "v1.10.2"
description = "File directory to store all data related processes for the Pokémon TCG."
readme = "README.md"
requires-python = ">=3.12"
Expand Down
27 changes: 14 additions & 13 deletions cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,18 +123,19 @@ func runCLI(args []string) int {

remainingArgs := mainFlagSet.Args()

commands := map[string]func() int{
"ability": utils.HandleCommandOutput(ability.AbilityCommand),
"berry": utils.HandleCommandOutput(berry.BerryCommand),
"card": utils.HandleCommandOutput(card.CardCommand),
"item": utils.HandleCommandOutput(item.ItemCommand),
"move": utils.HandleCommandOutput(move.MoveCommand),
"natures": utils.HandleCommandOutput(natures.NaturesCommand),
"pokemon": utils.HandleCommandOutput(pokemon.PokemonCommand),
"speed": utils.HandleCommandOutput(speed.SpeedCommand),
"tcg": utils.HandleCommandOutput(tcg.TcgCommand),
"types": utils.HandleCommandOutput(types.TypesCommand),
"search": utils.HandleCommandOutput(search.SearchCommand),
type commandFunc func([]string) (string, error)
commands := map[string]commandFunc{
"ability": ability.AbilityCommand,
"berry": berry.BerryCommand,
"card": card.CardCommand,
"item": item.ItemCommand,
"move": move.MoveCommand,
"natures": natures.NaturesCommand,
"pokemon": pokemon.PokemonCommand,
"speed": speed.SpeedCommand,
"tcg": tcg.TcgCommand,
"types": types.TypesCommand,
"search": search.SearchCommand,
}

cmdArg := ""
Expand All @@ -157,7 +158,7 @@ func runCLI(args []string) int {
currentVersion()
return 0
case exists:
return cmdFunc()
return utils.HandleCommandOutput(cmdFunc, remainingArgs)()
default:
msg := fmt.Sprintf("\t%-15s", fmt.Sprintf("'%s' is not a valid command.\n", cmdArg)) +
styling.StyleBold.Render("\nCommands:") +
Expand Down
4 changes: 0 additions & 4 deletions cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,6 @@ func TestRunCLI(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
originalArgs := os.Args
os.Args = append([]string{"poke-cli"}, tt.args...)
defer func() { os.Args = originalArgs }()

var exitCode int
output := captureOutput(func() {
exitCode = runCLI(tt.args)
Expand Down
20 changes: 10 additions & 10 deletions cmd/ability/ability.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"errors"
"flag"
"fmt"
"os"
"strings"

"github.com/digitalghost-dev/poke-cli/cmd/utils"
Expand All @@ -13,10 +12,10 @@ import (
"github.com/digitalghost-dev/poke-cli/styling"
)

func AbilityCommand() (string, error) {
func AbilityCommand(args []string) (string, error) {
var output strings.Builder

flag.Usage = func() {
usage := func() {
output.WriteString(
utils.GenerateHelpMessage(
utils.HelpConfig{
Expand All @@ -34,21 +33,22 @@ func AbilityCommand() (string, error) {

af := flags.SetupAbilityFlagSet()

args := os.Args

if utils.CheckHelpFlag(&output, flag.Usage) {
if utils.CheckHelpFlag(args, usage) {
return output.String(), nil
}

if err := utils.ValidateArgs(args, utils.Validator{MaxArgs: 4, CmdName: "ability", RequireName: true, HasFlags: true}); err != nil {
if err := utils.ValidateArgs(
args,
utils.Validator{MaxArgs: 3, CmdName: "ability", RequireName: true, HasFlags: true},
); err != nil {
output.WriteString(err.Error())
return output.String(), err
}

endpoint := strings.ToLower(args[1])
abilityName := strings.ToLower(args[2])
endpoint := strings.ToLower(args[0])
abilityName := strings.ToLower(args[1])

if err := af.FlagSet.Parse(args[3:]); err != nil {
if err := af.FlagSet.Parse(args[2:]); err != nil {
if errors.Is(err, flag.ErrHelp) {
return output.String(), nil
}
Expand Down
7 changes: 1 addition & 6 deletions cmd/ability/ability_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package ability

import (
"os"
"testing"

"github.com/digitalghost-dev/poke-cli/cmd/utils"
Expand Down Expand Up @@ -62,11 +61,7 @@ func TestAbilityCommand(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
originalArgs := os.Args
os.Args = append([]string{"poke-cli"}, tt.args...)
defer func() { os.Args = originalArgs }()

output, _ := AbilityCommand()
output, _ := AbilityCommand(tt.args)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Preserve error-path assertions in table tests.

Line 64 drops the returned err, so wantError is never enforced and negative-path regressions can pass unnoticed.

Suggested fix
-			output, _ := AbilityCommand(tt.args)
+			output, err := AbilityCommand(tt.args)
 			cleanOutput := styling.StripANSI(output)
+
+			if (err != nil) != tt.wantError {
+				t.Errorf("AbilityCommand() error = %v, wantErr %v", err, tt.wantError)
+				return
+			}
 
 			assert.Equal(t, tt.expectedOutput, cleanOutput, "Output should match expected")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
output, _ := AbilityCommand(tt.args)
output, err := AbilityCommand(tt.args)
cleanOutput := styling.StripANSI(output)
if (err != nil) != tt.wantError {
t.Errorf("AbilityCommand() error = %v, wantErr %v", err, tt.wantError)
return
}
assert.Equal(t, tt.expectedOutput, cleanOutput, "Output should match expected")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/ability/ability_test.go` at line 64, The test currently ignores the
returned error from AbilityCommand, so negative-path expectations (wantError)
aren't asserted; update the table-driven test to capture both outputs by
changing the call to retrieve (output, err) := AbilityCommand(tt.args) and then
assert err presence/absence matches tt.wantError (and optionally assert error
contents when wantError is true), referring to the AbilityCommand call and the
tt.wantError field in the test table to implement the check.

cleanOutput := styling.StripANSI(output)

assert.Equal(t, tt.expectedOutput, cleanOutput, "Output should match expected")
Expand Down
21 changes: 10 additions & 11 deletions cmd/berry/berry.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package berry

import (
"flag"
"fmt"
"os"
"strings"

"charm.land/bubbles/v2/table"
Expand All @@ -14,10 +12,10 @@ import (
"github.com/digitalghost-dev/poke-cli/styling"
)

func BerryCommand() (string, error) {
func BerryCommand(args []string) (string, error) {
var output strings.Builder

flag.Usage = func() {
usage := func() {
output.WriteString(
utils.GenerateHelpMessage(
utils.HelpConfig{
Expand All @@ -28,27 +26,28 @@ func BerryCommand() (string, error) {
)
}

if utils.CheckHelpFlag(&output, flag.Usage) {
if utils.CheckHelpFlag(args, usage) {
return output.String(), nil
}

flag.Parse()

// Validate arguments
if err := utils.ValidateArgs(os.Args, utils.Validator{MaxArgs: 4, CmdName: "berry", RequireName: false, HasFlags: false}); err != nil {
if err := utils.ValidateArgs(
args,
utils.Validator{MaxArgs: 3, CmdName: "berry", RequireName: false, HasFlags: false},
); err != nil {
output.WriteString(err.Error())
return output.String(), err
}

if len(os.Args) > 2 {
berryName := styling.CapitalizeResourceName(os.Args[2])
if len(args) > 1 {
berryName := styling.CapitalizeResourceName(args[1])
exists, err := berryExists(berryName)
if err != nil {
output.WriteString(utils.FormatError(err.Error()))
return output.String(), err
}
if !exists {
err := fmt.Errorf("berry %q not found", os.Args[2])
err := fmt.Errorf("berry %q not found", args[1])
output.WriteString(utils.FormatError(err.Error()))
return output.String(), err
}
Expand Down
27 changes: 6 additions & 21 deletions cmd/berry/berry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,32 +25,27 @@ func TestBerryCommand(t *testing.T) {
}{
{
name: "help flag short",
args: []string{"poke-cli", "berry", "-h"},
args: []string{"berry", "-h"},
wantErr: false,
contains: "USAGE:",
},
{
name: "help flag long",
args: []string{"poke-cli", "berry", "--help"},
args: []string{"berry", "--help"},
wantErr: false,
contains: "FLAGS:",
},
{
name: "invalid berry name",
args: []string{"poke-cli", "berry", "fakemon"},
args: []string{"berry", "fakemon"},
wantErr: true,
contains: "not found",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Set up os.Args for the test
oldArgs := os.Args
os.Args = tt.args
defer func() { os.Args = oldArgs }()

output, err := BerryCommand()
output, err := BerryCommand(tt.args)

if (err != nil) != tt.wantErr {
t.Errorf("BerryCommand() error = %v, wantErr %v", err, tt.wantErr)
Expand Down Expand Up @@ -312,11 +307,7 @@ func TestBerryCommandOutput(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
originalArgs := os.Args
os.Args = append([]string{"poke-cli"}, tt.args...)
defer func() { os.Args = originalArgs }()

output, err := BerryCommand()
output, err := BerryCommand(tt.args)
require.NoError(t, err, "BerryCommand failed: %v", err)
cleanOutput := styling.StripANSI(output)

Expand All @@ -326,13 +317,7 @@ func TestBerryCommandOutput(t *testing.T) {
}

func TestBerryCommandValidationError(t *testing.T) {
originalArgs := os.Args
defer func() { os.Args = originalArgs }()

// Set os.Args with extra argument to trigger validation error
os.Args = []string{"poke-cli", "berry", "cheri", "extra-arg"}

output, err := BerryCommand()
output, err := BerryCommand([]string{"berry", "cheri", "extra-arg"})
require.Error(t, err, "BerryCommand should return error for invalid args")
assert.Contains(t, output, "Error", "Output should contain error message")
}
13 changes: 11 additions & 2 deletions cmd/berry/berryinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package berry

import (
"image"
"io"
"net/http"
"strings"

Expand All @@ -11,6 +12,10 @@ import (
"github.com/disintegration/imaging"
)

const maxBerryImageBytes = 5 * 1024 * 1024 // 5 MiB

var berryImageHTTPClient = connections.NewDefaultHTTPClient()

func berryExists(name string) (bool, error) {
results, err := connections.QueryBerryData(`
SELECT 1 FROM berries
Expand Down Expand Up @@ -121,13 +126,17 @@ func berryImage(berryName string) string {
return str.String()
}

imageResp, err := http.Get(berryImage[0])
imageResp, err := berryImageHTTPClient.Get(berryImage[0])
if err != nil {
return "Error downloading berry image"
}
defer imageResp.Body.Close()

img, err := imaging.Decode(imageResp.Body)
if imageResp.StatusCode != http.StatusOK {
return "Error downloading berry image"
}

img, err := imaging.Decode(io.LimitReader(imageResp.Body, maxBerryImageBytes))
if err != nil {
return "Error decoding berry image"
}
Expand Down
Loading
Loading