diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 68ff96c5..362b3335 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ on: - main env: - VERSION_NUMBER: 'v1.7.3' + VERSION_NUMBER: 'v1.7.4' DOCKERHUB_REGISTRY_NAME: 'digitalghostdev/poke-cli' AWS_REGION: 'us-west-2' diff --git a/.goreleaser.yml b/.goreleaser.yml index 38286ad4..04c0950f 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -14,7 +14,7 @@ builds: - windows - darwin ldflags: - - -s -w -X main.version=v1.7.3 + - -s -w -X main.version=v1.7.4 archives: - formats: [ 'zip' ] diff --git a/Dockerfile b/Dockerfile index 3b3f34f2..8db3b266 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # build 1 -FROM golang:1.24.6-alpine3.22 AS build +FROM golang:1.24.9-alpine3.22 AS build WORKDIR /app @@ -8,7 +8,7 @@ RUN go mod download COPY . . -RUN go build -ldflags "-X main.version=v1.7.3" -o poke-cli . +RUN go build -ldflags "-X main.version=v1.7.4" -o poke-cli . # build 2 FROM --platform=$BUILDPLATFORM alpine:3.22 diff --git a/README.md b/README.md index 1f939c28..a4529df4 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ pokemon-logo

Pokémon CLI

version-label - docker-image-size + docker-image-size ci-status-badge
@@ -94,11 +94,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.7.3 [subcommand] flag] + docker run --rm -it digitalghostdev/poke-cli:v1.7.4 [subcommand] flag] ``` * Enter the container and use its shell: ```bash - docker run --rm -it --name poke-cli --entrypoint /bin/sh digitalghostdev/poke-cli:v1.7.3 -c "cd /app && exec sh" + docker run --rm -it --name poke-cli --entrypoint /bin/sh digitalghostdev/poke-cli:v1.7.4 -c "cd /app && exec sh" # placed into the /app directory, run the program with './poke-cli' # example: ./poke-cli ability swift-swim ``` diff --git a/card_data/pipelines/poke_cli_dbt/dbt_project.yml b/card_data/pipelines/poke_cli_dbt/dbt_project.yml index 74a0c6c4..104f295f 100644 --- a/card_data/pipelines/poke_cli_dbt/dbt_project.yml +++ b/card_data/pipelines/poke_cli_dbt/dbt_project.yml @@ -1,5 +1,5 @@ name: 'poke_cli_dbt' -version: '1.7.3' +version: '1.7.4' profile: 'poke_cli_dbt' diff --git a/cli.go b/cli.go index 2752f612..cca5a412 100644 --- a/cli.go +++ b/cli.go @@ -130,7 +130,10 @@ func runCLI(args []string) int { mainFlagSet.Usage() return 1 case *latestFlag || *shortLatestFlag: - _, _ = flags.LatestFlag() + _, err := flags.LatestFlag() + if err != nil { + return 1 + } return 0 case *currentVersionFlag || *shortCurrentVersionFlag: currentVersion() diff --git a/cmd/utils/validateargs_test.go b/cmd/utils/validateargs_test.go index 8094c978..99c60dc0 100644 --- a/cmd/utils/validateargs_test.go +++ b/cmd/utils/validateargs_test.go @@ -182,6 +182,125 @@ func TestValidatePokemonArgs(t *testing.T) { } } +// TestValidateBerryArgs tests the ValidateBerryArgs function +func TestValidateBerryArgs(t *testing.T) { + validInputs := [][]string{ + {"poke-cli", "berry"}, + {"poke-cli", "berry", "--help"}, + } + + for _, input := range validInputs { + err := ValidateBerryArgs(input) + require.NoError(t, err, "Expected no error for valid input") + } + + invalidInputs := [][]string{ + {"poke-cli", "berry", "oran"}, + } + + for _, input := range invalidInputs { + err := ValidateBerryArgs(input) + require.Error(t, err, "Expected error for invalid input") + } + + tooManyArgs := [][]string{ + {"poke-cli", "berry", "oran", "sitrus"}, + } + + expectedError := styling.StripANSI("╭──────────────────╮\n│✖ Error! │\n│Too many arguments│\n╰──────────────────╯") + + for _, input := range tooManyArgs { + err := ValidateBerryArgs(input) + + if err == nil { + t.Fatalf("Expected an error for input %v, but got nil", input) + } + + strippedErr := styling.StripANSI(err.Error()) + assert.Equal(t, expectedError, strippedErr, "Unexpected error message for invalid input") + } +} + +// TestValidateItemArgs tests the ValidateItemArgs function +func TestValidateItemArgs(t *testing.T) { + validInputs := [][]string{ + {"poke-cli", "item", "--help"}, + {"poke-cli", "item", "potion"}, + {"poke-cli", "item", "master-ball"}, + } + + for _, input := range validInputs { + err := ValidateItemArgs(input) + require.NoError(t, err, "Expected no error for valid input") + } + + invalidInputs := [][]string{ + {"poke-cli", "item"}, + } + + for _, input := range invalidInputs { + err := ValidateItemArgs(input) + require.Error(t, err, "Expected error for invalid input") + } + + tooManyArgs := [][]string{ + {"poke-cli", "item", "potion", "super-potion"}, + } + + expectedError := styling.StripANSI("╭──────────────────╮\n│✖ Error! │\n│Too many arguments│\n╰──────────────────╯") + + for _, input := range tooManyArgs { + err := ValidateItemArgs(input) + + if err == nil { + t.Fatalf("Expected an error for input %v, but got nil", input) + } + + strippedErr := styling.StripANSI(err.Error()) + assert.Equal(t, expectedError, strippedErr, "Unexpected error message for invalid input") + } +} + +// TestValidateMoveArgs tests the ValidateMoveArgs function +func TestValidateMoveArgs(t *testing.T) { + validInputs := [][]string{ + {"poke-cli", "move", "--help"}, + {"poke-cli", "move", "thunderbolt"}, + {"poke-cli", "move", "Dragon-Tail"}, + } + + for _, input := range validInputs { + err := ValidateMoveArgs(input) + require.NoError(t, err, "Expected no error for valid input") + } + + invalidInputs := [][]string{ + {"poke-cli", "move"}, + } + + for _, input := range invalidInputs { + err := ValidateMoveArgs(input) + require.Error(t, err, "Expected error for invalid input") + } + + tooManyArgs := [][]string{ + {"poke-cli", "move", "tackle", "scratch"}, + } + + expectedError := styling.StripANSI("╭──────────────────╮\n│✖ Error! │\n│Too many arguments│\n╰──────────────────╯") + + for _, input := range tooManyArgs { + err := ValidateMoveArgs(input) + + if err == nil { + t.Fatalf("Expected an error for input %v, but got nil", input) + } + + strippedErr := styling.StripANSI(err.Error()) + assert.Equal(t, expectedError, strippedErr, "Unexpected error message for invalid input") + } +} + // TestValidateSearchArgs tests the ValidateSearchArgs function func TestValidateSearchArgs(t *testing.T) { validInputs := [][]string{ @@ -262,3 +381,42 @@ func TestValidateTypesArgs(t *testing.T) { assert.Equal(t, expectedError, strippedErr, "Unexpected error message for invalid input") } } + +// TestValidateSpeedArgs tests the ValidateSpeedArgs function +func TestValidateSpeedArgs(t *testing.T) { + validInputs := [][]string{ + {"poke-cli", "speed"}, + {"poke-cli", "speed", "--help"}, + } + + for _, input := range validInputs { + err := ValidateSpeedArgs(input) + require.NoError(t, err, "Expected no error for valid input") + } + + invalidInputs := [][]string{ + {"poke-cli", "speed", "100"}, + } + + for _, input := range invalidInputs { + err := ValidateSpeedArgs(input) + require.Error(t, err, "Expected error for invalid input") + } + + tooManyArgs := [][]string{ + {"poke-cli", "speed", "100", "200"}, + } + + expectedError := styling.StripANSI("╭──────────────────╮\n│✖ Error! │\n│Too many arguments│\n╰──────────────────╯") + + for _, input := range tooManyArgs { + err := ValidateSpeedArgs(input) + + if err == nil { + t.Fatalf("Expected an error for input %v, but got nil", input) + } + + strippedErr := styling.StripANSI(err.Error()) + assert.Equal(t, expectedError, strippedErr, "Unexpected error message for invalid input") + } +} diff --git a/flags/version.go b/flags/version.go index a29d0db8..aa213518 100644 --- a/flags/version.go +++ b/flags/version.go @@ -2,6 +2,7 @@ package flags import ( "encoding/json" + "errors" "flag" "fmt" "io" @@ -16,58 +17,62 @@ import ( func LatestFlag() (string, error) { var output strings.Builder - latestRelease(&output) + err := latestRelease(&output) result := output.String() fmt.Print(result) - return result, nil + return result, err } -func latestRelease(output *strings.Builder) { +func latestRelease(output *strings.Builder) error { type Release struct { TagName string `json:"tag_name"` } - // Parse and validate the URL parsedURL, err := url.Parse("https://api.github.com/repos/digitalghost-dev/poke-cli/releases/latest") if err != nil { - fmt.Fprintf(output, "invalid URL: %v\n", err) - return + err = fmt.Errorf("invalid URL: %w", err) + fmt.Fprintln(output, err) + return err } - // Implementing gosec error if flag.Lookup("test.v") == nil { if parsedURL.Scheme != "https" { - fmt.Fprint(output, "only HTTPS URLs are allowed for security reasons\n") - return + err := errors.New("only HTTPS URLs are allowed for security reasons") + fmt.Fprintln(output, err) + return err } if parsedURL.Host != "api.github.com" { - fmt.Fprint(output, "url host is not allowed\n") - return + err := errors.New("url host is not allowed") + fmt.Fprintln(output, err) + return err } } response, err := http.Get(parsedURL.String()) if err != nil { - fmt.Fprintf(output, "error fetching data: %v\n", err) - return + err = fmt.Errorf("error fetching data: %w", err) + fmt.Fprintln(output, err) + return err } defer response.Body.Close() body, err := io.ReadAll(response.Body) if err != nil { - fmt.Fprintf(output, "error reading response body: %v\n", err) - return + err = fmt.Errorf("error reading response body: %w", err) + fmt.Fprintln(output, err) + return err } var release Release if err := json.Unmarshal(body, &release); err != nil { - fmt.Fprintf(output, "error unmarshalling JSON: %v\n", err) - return + err = fmt.Errorf("error unmarshalling JSON: %w", err) + fmt.Fprintln(output, err) + return err } - releaseString := "Latest available version:" + releaseString := "Latest available release on GitHub:" releaseTag := styling.ColoredBullet.Render("") + release.TagName docStyle := lipgloss.NewStyle(). @@ -80,4 +85,6 @@ func latestRelease(output *strings.Builder) { output.WriteString(docStyle.Render(fullDoc)) output.WriteString("\n") + + return nil } diff --git a/flags/version_test.go b/flags/version_test.go index fef1b1e7..869c1279 100644 --- a/flags/version_test.go +++ b/flags/version_test.go @@ -7,6 +7,7 @@ import ( "github.com/digitalghost-dev/poke-cli/cmd/utils" "github.com/digitalghost-dev/poke-cli/styling" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestLatestVersionFlag(t *testing.T) { @@ -32,11 +33,13 @@ func TestLatestVersionFlag(t *testing.T) { name: "Get latest version with short flag", args: []string{"-l"}, expectedOutput: utils.LoadGolden(t, "main_latest_flag.golden"), + expectedError: false, }, { name: "Get latest version with long flag", args: []string{"--latest"}, expectedOutput: utils.LoadGolden(t, "main_latest_flag.golden"), + expectedError: false, }, } @@ -46,9 +49,15 @@ func TestLatestVersionFlag(t *testing.T) { os.Args = append([]string{"poke-cli"}, tt.args...) defer func() { os.Args = originalArgs }() - output, _ := LatestFlag() + output, err := LatestFlag() cleanOutput := styling.StripANSI(output) + if tt.expectedError { + require.Error(t, err, "Expected an error") + } else { + require.NoError(t, err, "Expected no error") + } + assert.Equal(t, tt.expectedOutput, cleanOutput, "Output should match expected") }) } diff --git a/go.mod b/go.mod index 5b5957b2..6dfd4ea1 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/digitalghost-dev/poke-cli -go 1.24.6 +go 1.24.9 require ( github.com/charmbracelet/bubbles v0.21.0 diff --git a/nfpm.yaml b/nfpm.yaml index 9949708c..d5ff4ad5 100644 --- a/nfpm.yaml +++ b/nfpm.yaml @@ -1,7 +1,7 @@ name: "poke-cli" arch: "arm64" platform: "linux" -version: "v1.7.3" +version: "v1.7.4" section: "default" version_schema: semver maintainer: "Christian S" diff --git a/styling/styling_test.go b/styling/styling_test.go index 0df10ba6..efd7f501 100644 --- a/styling/styling_test.go +++ b/styling/styling_test.go @@ -97,3 +97,20 @@ func TestColor_Hex(t *testing.T) { t.Errorf("Expected %s, got %s", expected, hex) } } + +func TestFormTheme(t *testing.T) { + theme := FormTheme() + + assert.NotNil(t, theme, "FormTheme should return a non-nil theme") + assert.NotNil(t, theme.Focused, "Focused state should be configured") + assert.NotNil(t, theme.Blurred, "Blurred state should be configured") + assert.NotNil(t, theme.Group, "Group state should be configured") + + focusedButtonStyle := theme.Focused.FocusedButton + assert.NotNil(t, focusedButtonStyle, "Focused button style should be set") + + assert.Equal(t, theme.Focused.FocusedButton, theme.Focused.Next, "Next button should use focused button style") + assert.NotNil(t, theme.Blurred.Base, "Blurred base should be configured") + assert.Equal(t, theme.Focused.Title, theme.Group.Title, "Group title should match focused title") + assert.Equal(t, theme.Focused.Description, theme.Group.Description, "Group description should match focused description") +} diff --git a/testdata/main_latest_flag.golden b/testdata/main_latest_flag.golden index 5f94b689..262d4786 100644 --- a/testdata/main_latest_flag.golden +++ b/testdata/main_latest_flag.golden @@ -1,6 +1,7 @@ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ ┃ -┃ Latest available version: ┃ -┃ • v1.7.2 ┃ +┃ Latest available release ┃ +┃ on GitHub: ┃ +┃ • v1.7.3 ┃ ┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛