diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 578d575..c86a98e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,6 +16,8 @@ on: - 'README.md' - '.github/**' - '.dockerignore' + - 'docs/**' + - 'etl/**' - '.gitignore' - 'demo**' - 'go.mod' @@ -26,7 +28,7 @@ on: - main env: - VERSION_NUMBER: 'v1.3.2' + VERSION_NUMBER: 'v1.3.3' DOCKERHUB_REGISTRY_NAME: 'digitalghostdev/poke-cli' AWS_REGION: 'us-west-2' @@ -53,7 +55,77 @@ jobs: with: sarif_file: results.sarif - build-docker-image: + build-docs-docker-image: + runs-on: ubuntu-22.04 + needs: [ gosec ] + if: needs.gosec.result == 'success' + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + sparse-checkout: | + docs + + - name: Set up Docker Buildx + uses: 'docker/setup-buildx-action@v3.0.0' + + - name: Prepare Docker Build Context + run: | + mkdir docker-context + rsync -av --exclude=docker-context . docker-context/ + + - name: Build and Export + uses: 'docker/build-push-action@v5.0.0' + with: + context: ./docker-context + file: ./docker-context/docs/Dockerfile + tags: docs:latest + outputs: type=docker,dest=/tmp/docs.tar + + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: docs + path: /tmp/docs.tar + + upload-docs-to-ecr: + runs-on: ubuntu-22.04 + needs: [build-docs-docker-image] + if: needs.build-docs-docker-image.result == 'success' + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Configure AWS + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v2 + + - name: Download Artifact + uses: actions/download-artifact@v4 + with: + name: docs + path: /tmp + + - name: Load Image + run: docker load -i /tmp/docs.tar + + - name: Tag and Push + run: | + docker tag docs:latest ${{ secrets.AWS_DOCS_ECR_NAME }}:latest + docker push ${{ secrets.AWS_DOCS_ECR_NAME }}:latest + + # AWS will then take care of updating App Runner with the latest version + + build-cli-docker-image: runs-on: ubuntu-22.04 needs: [gosec] if: needs.gosec.result == 'success' @@ -83,11 +155,11 @@ jobs: name: poke-cli path: /tmp/poke-cli.tar - # Uploading to Elastic Container Registry has a backup method. - upload-to-ecr: + # Uploading to Elastic Container Registry as a backup method. + upload-cli-to-ecr: runs-on: ubuntu-22.04 - needs: [build-docker-image] - if: needs.build-docker-image.result == 'success' + needs: [build-cli-docker-image] + if: needs.build-cli-docker-image.result == 'success' steps: - name: Checkout @@ -116,8 +188,8 @@ jobs: id-token: 'write' runs-on: ubuntu-22.04 - needs: [build-docker-image] - if: needs.build-docker-image.result == 'success' + needs: [build-cli-docker-image] + if: needs.build-cli-docker-image.result == 'success' steps: - name: Checkout diff --git a/.gitignore b/.gitignore index 1ef22dd..33bfbea 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,7 @@ go.work go.work.sum # env file -.env \ No newline at end of file +.env + +# Python +etl/.venv \ No newline at end of file diff --git a/.goreleaser.yaml b/.goreleaser.yaml index f73112f..e137734 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -14,10 +14,10 @@ builds: - windows - darwin ldflags: - - -s -w -X main.version=v1.3.2 + - -s -w -X main.version=v1.3.3 archives: - - format: tar.gz + - formats: [ 'zip' ] name_template: >- {{ .ProjectName }}_ {{- title .Os }}_ @@ -28,7 +28,7 @@ archives: # use zip for windows archives format_overrides: - goos: windows - format: zip + - formats: [ 'zip' ] changelog: sort: asc @@ -37,11 +37,20 @@ changelog: - "^docs:" - "^test:" -brews: - - repository: +homebrew_casks: + - name: poke-cli + conflicts: + - formula: poke-cli + repository: owner: digitalghost-dev name: homebrew-poke-cli token: "{{.Env.GITHUB_TOKEN}}" homepage: "https://github.com/digitalghost-dev/poke-cli" - description: "A CLI tool written in Go that allows you to view data about Pokémon from the terminal." + description: "A hybrid CLI/TUI tool written in Go for viewing Pokémon data from the terminal!" license: "Apache License 2.0" + hooks: + post: + install: | + if system_command("/usr/bin/xattr", args: ["-h"]).exit_status == 0 + system_command "/usr/bin/xattr", args: ["-dr", "com.apple.quarantine", "#{staged_path}/poke-cli"] + end \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 5b30230..8263051 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,18 @@ -# build 1 -FROM golang:1.24.4-alpine3.21 AS build +# Stage 1: Dependencies +FROM golang:1.24.4-alpine3.21 AS deps WORKDIR /app COPY go.mod go.sum ./ -RUN go mod download +RUN go mod tidy +# Stage 2: Build +FROM deps AS build-stage COPY . . -RUN go build -ldflags "-X main.version=v1.3.2" -o poke-cli . +RUN go build -ldflags "-X main.version=v1.3.3" -o poke-cli . -# build 2 +# Stage 3: Production FROM --platform=$BUILDPLATFORM alpine:latest # Install only necessary packages and remove them after use @@ -19,7 +21,7 @@ RUN apk add --no-cache shadow && \ sed -i 's/^root:.*/root:!*:0:0:root:\/root:\/sbin\/nologin/' /etc/passwd && \ apk del shadow -COPY --from=build /app/poke-cli /app/poke-cli +COPY --from=build-stage /app/poke-cli /app/poke-cli ENV TERM=xterm-256color ENV COLOR_OUTPUT=true diff --git a/README.md b/README.md index e1fc1bd..6b7a0d0 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
@@ -13,6 +13,7 @@ ## Overview `poke-cli` is a hybrid of a classic CLI and a modern TUI tool for viewing data about Pokémon! This is my first Go project. +View the [documentation](https://docs.poke-cli.com)! The architecture behind how the tool works is straight forward: 1. Each command indicates which [API](https://pokeapi.co/) endpoint to use. @@ -23,7 +24,7 @@ View future plans in the [Roadmap](#roadmap) section. --- ## Demo -![demo](https://poke-cli-s3-bucket.s3.us-west-2.amazonaws.com/demo-v1.2.1.gif) +![demo](https://poke-cli-s3-bucket.s3.us-west-2.amazonaws.com/demo-v1.3.3.gif) --- ## Installation @@ -76,11 +77,11 @@ View future plans in the [Roadmap](#roadmap) section. 3. Choose how to interact with the container: * Run a single command and exit: ```bash - docker run --rm -it digitalghostdev/poke-cli:v1.3.2 [subcommand] flag] + docker run --rm -it digitalghostdev/poke-cli:v1.3.3 [subcommand] flag] ``` * Enter the container and use its shell: ```bash - docker run --rm -it --name poke-cli --entrypoint /bin/sh digitalghostdev/poke-cli:v1.3.2 -c "cd /app && exec sh" + docker run --rm -it --name poke-cli --entrypoint /bin/sh digitalghostdev/poke-cli:v1.3.3 -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/cli.go b/cli.go index 827a08d..9a489c0 100644 --- a/cli.go +++ b/cli.go @@ -74,6 +74,7 @@ func runCLI(args []string) int { "\n\n", styling.StyleItalic.Render("hint: when calling a resource with a space, use a hyphen"), "\n", styling.StyleItalic.Render("example: poke-cli ability strong-jaw"), "\n", styling.StyleItalic.Render("example: poke-cli pokemon flutter-mane"), + "\n\n", fmt.Sprintf("%s %s", "↓ ctrl/cmd + click for docs/guides\n", styling.DocsLink), ) fmt.Println(helpMessage) } diff --git a/cmd/ability/ability.go b/cmd/ability/ability.go index 06c0c19..4a5cc30 100644 --- a/cmd/ability/ability.go +++ b/cmd/ability/ability.go @@ -88,21 +88,21 @@ func AbilityCommand() (string, error) { capitalizedAbility := cases.Title(language.English).String(strings.ReplaceAll(abilityName, "-", " ")) output.WriteString(styling.StyleBold.Render(capitalizedAbility) + "\n") - // API is missing some data for the short_effect for abilities from Generation 9. - // If short_effect is empty, fallback to the move's flavor_text_entry. - if englishShortEffect == "" { - output.WriteString("Effect: " + englishFlavorEntry + "\n") - } else { - output.WriteString("Effect: " + englishShortEffect + "\n") - } - - // Print the generation where the move was first introduced. + // Print the generation where the ability was first introduced. generationParts := strings.Split(abilitiesStruct.Generation.Name, "-") if len(generationParts) > 1 { generationUpper := strings.ToUpper(generationParts[1]) - output.WriteString("Generation: " + generationUpper + "\n") + output.WriteString(fmt.Sprintf("%s First introduced in generation "+generationUpper+"\n", styling.ColoredBullet)) + } else { + output.WriteString(fmt.Sprintf("%s Generation: Unknown\n", styling.ColoredBullet)) + } + + // API is missing some data for the short_effect for abilities from Generation 9. + // If short_effect is empty, fallback to the move's flavor_text_entry. + if englishShortEffect == "" { + output.WriteString(fmt.Sprintf("%s Effect: "+englishFlavorEntry, styling.ColoredBullet)) } else { - output.WriteString("Generation: Unknown\n") + output.WriteString(fmt.Sprintf("%s Effect: "+englishShortEffect, styling.ColoredBullet)) } if *pokemonFlag || *shortPokemonFlag { diff --git a/demo.gif b/demo.gif index 8fed910..adeeec4 100644 Binary files a/demo.gif and b/demo.gif differ diff --git a/demo.tape b/demo.tape index bcc6aba..a040354 100644 --- a/demo.tape +++ b/demo.tape @@ -63,7 +63,8 @@ Set Shell "bash" Set FontSize 32 Set Width 2100 Set Height 1300 -Set TypingSpeed 85ms +Set TypingSpeed 100ms +Set Theme "tokyonight-storm" Type "poke-cli pokemon charizard --abilities --stats --types" diff --git a/docs/.dockerignore b/docs/.dockerignore new file mode 100644 index 0000000..cbdbf54 --- /dev/null +++ b/docs/.dockerignore @@ -0,0 +1,3 @@ +# Environments +.env +.venv \ No newline at end of file diff --git a/docs/Dockerfile b/docs/Dockerfile new file mode 100644 index 0000000..0dfc12d --- /dev/null +++ b/docs/Dockerfile @@ -0,0 +1,23 @@ +# Dockerfile.prod +FROM python:3.12-slim AS builder + +WORKDIR /build + +RUN pip install mkdocs mkdocs-material + +COPY mkdocs.yml /build/mkdocs.yml + +COPY docs/ /build/docs/ + +RUN mkdocs build + +# --- Serve with lightweight HTTP server --- +FROM python:3.12-slim + +WORKDIR /site + +COPY --from=builder /build/site /site + +EXPOSE 8080 + +CMD ["python3", "-m", "http.server", "8080"] \ No newline at end of file diff --git a/docs/assets/DigitalGhostLogo-512.png b/docs/assets/DigitalGhostLogo-512.png new file mode 100644 index 0000000..d984cc5 Binary files /dev/null and b/docs/assets/DigitalGhostLogo-512.png differ diff --git a/docs/assets/ability.gif b/docs/assets/ability.gif new file mode 100644 index 0000000..dd8d18f Binary files /dev/null and b/docs/assets/ability.gif differ diff --git a/docs/assets/move.gif b/docs/assets/move.gif new file mode 100644 index 0000000..b8b53f4 Binary files /dev/null and b/docs/assets/move.gif differ diff --git a/docs/assets/natures.gif b/docs/assets/natures.gif new file mode 100644 index 0000000..c0f33b8 Binary files /dev/null and b/docs/assets/natures.gif differ diff --git a/docs/assets/pokemon_abilities_moves.gif b/docs/assets/pokemon_abilities_moves.gif new file mode 100644 index 0000000..8d51adf Binary files /dev/null and b/docs/assets/pokemon_abilities_moves.gif differ diff --git a/docs/assets/pokemon_image.gif b/docs/assets/pokemon_image.gif new file mode 100644 index 0000000..1fc59a7 Binary files /dev/null and b/docs/assets/pokemon_image.gif differ diff --git a/docs/assets/pokemon_stats_types.gif b/docs/assets/pokemon_stats_types.gif new file mode 100644 index 0000000..68d9932 Binary files /dev/null and b/docs/assets/pokemon_stats_types.gif differ diff --git a/docs/assets/search.gif b/docs/assets/search.gif new file mode 100644 index 0000000..502cb20 Binary files /dev/null and b/docs/assets/search.gif differ diff --git a/docs/assets/types.gif b/docs/assets/types.gif new file mode 100644 index 0000000..1871281 Binary files /dev/null and b/docs/assets/types.gif differ diff --git a/docs/commands.md b/docs/commands.md new file mode 100644 index 0000000..04008cc --- /dev/null +++ b/docs/commands.md @@ -0,0 +1,128 @@ +# Commands + +## main + +**Available Flags** + +* `--help | -h` +* `--latest | -l` +* `--version | -v` + +--- + +## `ability` +* Retrieve information about a specific ability, including its flavor text, +the generation in which it first appeared, and a list of Pokémon that possess it. + +**Available Flags** + +* `--help | -h` +* `--pokemon | -p` + +Example: +```console +$ poke-cli ability solar-power +$ poke-cli ability solar-power --pokemon # list Pokémon that posses the ability +``` + +Output: + +![ability_command](assets/ability.gif) + +--- + +## `move` +* Retrieve information about a specific move, including its type, power, PP, accuracy, category, etc., +and the move's effect. + +Example: +```console +$ poke-cli move dazzling-gleam +``` + +Output: + +![move_command](assets/move.gif) + +--- + +## `natures` +* Retrieve a table of all natures and the stats they affect. + +Example: +```console +$ poke-cli natures +``` + +Output: + +![natures_gif](assets/natures.gif) + +--- + +## `pokemon` +* Retrieve information about a specific Pokémon such as available abilities, learnable moves, typing, and base stats. All data is based on generation 9. + +**Available Flags** + +* `--help | -h` +* `--abilities | -a` +* `--image=xx | -i=xx` +* `--moves | -m` +* `--stats | -s` +* `--types | -t` + +Example: +```console +$ poke-cli pokemon rockruff --abilities --moves +``` + +Output: + +![pokemon_abilities_moves](assets/pokemon_abilities_moves.gif) + +Example: +```shell +# choose between three sizes: 'sm', 'md', 'lg' +$ poke-cli pokemon tyranitar --image=sm +``` + +Output: + +![pokemon_image](assets/pokemon_image.gif) + +Example: +```console +$ poke-cli pokemon cacturne --stats --types +``` + +Output: + +![pokemon_stats_types](assets/pokemon_stats_types.gif) + +--- + +## `search` +* Search for resources from different endpoints. Searchable endpoints include `ability`, `pokemon`, and `move`. + +Example: +```console +$ poke-cli search +``` + +Output: + +![search_command](assets/search.gif) + +--- + +## `types` +* Retrieve details about a specific type and a damage relation table. + +Example: +```console +$ poke-cli types +``` +Output: + +![types_command](assets/types.gif) \ No newline at end of file diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..4ac45b3 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,21 @@ +# Overview + +**Welcome!** + +This site is the central hub for detailed information on how to install, use, and learn about this project. + +The project originally began as a way to explore a new programming language to complement my existing skills in Python and SQL. +I chose Go (Golang) and set out to build a CLI/TUI tool. + +Since then, the scope has expanded beyond a simple CLI/TUI. It now incorporates DevOps and Data Engineering practices, +including CI/CD pipelines with GitHub Actions and data workflows using Airflow and AWS. + +## Demo +Here is a quick demo of the CLI/TUI tool in action. +![demo_gif](https://poke-cli-s3-bucket.s3.us-west-2.amazonaws.com/demo-v1.3.3.gif) + +## CI/CD Infrastructure +*coming soon* + +## Data Infrastructure +*coming soon* \ No newline at end of file diff --git a/flags/abilityflagset.go b/flags/abilityflagset.go index 338ff1f..8f6fad0 100644 --- a/flags/abilityflagset.go +++ b/flags/abilityflagset.go @@ -33,7 +33,7 @@ func PokemonAbilitiesFlag(w io.Writer, endpoint string, abilityName string) erro capitalizedEffect := cases.Title(language.English).String(strings.ReplaceAll(abilityName, "-", " ")) - if _, err := fmt.Fprintf(w, "\n%s\n\n", styling.StyleUnderline.Render("Pokemon with "+capitalizedEffect)); err != nil { + if _, err := fmt.Fprintf(w, "\n\n%s\n\n", styling.StyleUnderline.Render("Pokemon with "+capitalizedEffect)); err != nil { return err } diff --git a/go.mod b/go.mod index 1a6c1bd..18cf049 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/charmbracelet/bubbles v0.20.0 github.com/charmbracelet/bubbletea v1.2.4 github.com/charmbracelet/lipgloss v1.1.0 + github.com/charmbracelet/x/exp/teatest v0.0.0-20250317102001-c803e5cafd0b github.com/charmbracelet/x/term v0.2.1 github.com/disintegration/imaging v1.6.2 github.com/stretchr/testify v1.10.0 @@ -20,7 +21,6 @@ require ( github.com/charmbracelet/x/ansi v0.8.0 // indirect github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect github.com/charmbracelet/x/exp/golden v0.0.0-20241212170349-ad4b7ae0f25f // indirect - github.com/charmbracelet/x/exp/teatest v0.0.0-20250317102001-c803e5cafd0b // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..a73ff08 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,49 @@ +site_name: poke-cli + +theme: + name: material + palette: + # Palette toggle for automatic mode + - media: "(prefers-color-scheme)" + toggle: + icon: material/brightness-auto + name: Switch to light mode + + # Palette toggle for light mode + - media: "(prefers-color-scheme: light)" + scheme: default + toggle: + icon: material/brightness-7 + name: Switch to dark mode + + # Palette toggle for dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + toggle: + icon: material/brightness-4 + name: Switch to system preference + primary: red + features: + - navigation.footer + logo: + assets/DigitalGhostLogo-512.png + +repo_url: https://github.com/digitalghost-dev/poke-cli + +markdown_extensions: + - attr_list + - pymdownx.emoji: + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg + - pymdownx.highlight: + anchor_linenums: true + line_spans: __span + pygments_lang_class: true + - pymdownx.inlinehilite + - pymdownx.snippets + - pymdownx.superfences + +extra: + social: + - icon: fontawesome/brands/docker + link: https://hub.docker.com/r/digitalghostdev/poke-cli \ No newline at end of file diff --git a/styling/styling.go b/styling/styling.go index ee8b2d2..cdb1a68 100644 --- a/styling/styling.go +++ b/styling/styling.go @@ -17,6 +17,10 @@ var ( CheckboxStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#FFCC00")) KeyMenu = lipgloss.NewStyle().Foreground(lipgloss.Color("#777777")) + DocsLink = lipgloss.NewStyle(). + Foreground(lipgloss.AdaptiveColor{Light: "#E1AD01", Dark: "#FFCC00"}). + Render("\x1b]8;;https://docs.poke-cli.com\x1b\\docs.poke-cli.com\x1b]8;;\x1b\\") + StyleBold = lipgloss.NewStyle().Bold(true) StyleItalic = lipgloss.NewStyle().Italic(true) StyleUnderline = lipgloss.NewStyle().Underline(true) diff --git a/testdata/ability.golden b/testdata/ability.golden index b5e0b5d..fcf850a 100644 --- a/testdata/ability.golden +++ b/testdata/ability.golden @@ -1,3 +1,3 @@ Clear Body -Effect: Prevents stats from being lowered by other Pokémon. -Generation: III +• First introduced in generation III +• Effect: Prevents stats from being lowered by other Pokémon. \ No newline at end of file diff --git a/testdata/ability_flag_pokemon.golden b/testdata/ability_flag_pokemon.golden index 46e7bf8..c215a6d 100644 --- a/testdata/ability_flag_pokemon.golden +++ b/testdata/ability_flag_pokemon.golden @@ -1,6 +1,6 @@ Anger Point -Effect: Raises Attack to the maximum of six stages upon receiving a critical hit. -Generation: IV +• First introduced in generation IV +• Effect: Raises Attack to the maximum of six stages upon receiving a critical hit. Pokemon with Anger Point diff --git a/testdata/cli_help.golden b/testdata/cli_help.golden index c116ad4..43bc33d 100644 --- a/testdata/cli_help.golden +++ b/testdata/cli_help.golden @@ -22,4 +22,7 @@ │ hint: when calling a resource with a space, use a hyphen │ │ example: poke-cli ability strong-jaw │ │ example: poke-cli pokemon flutter-mane │ +│ │ +│ ↓ ctrl/cmd + click for docs/guides │ +│ ]8;;https://docs.poke-cli.com\docs.poke-cli.com]8;;\ │ ╰──────────────────────────────────────────────────────────╯