diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 1bc18d1a..8ff0f94f 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -31,7 +31,7 @@ on:
- main
env:
- VERSION_NUMBER: 'v1.8.8'
+ VERSION_NUMBER: 'v1.8.9'
DOCKERHUB_REGISTRY_NAME: 'digitalghostdev/poke-cli'
AWS_REGION: 'us-west-2'
diff --git a/.github/workflows/go_lint.yml b/.github/workflows/go_lint.yml
index 3b97a1f2..6c06f7f7 100644
--- a/.github/workflows/go_lint.yml
+++ b/.github/workflows/go_lint.yml
@@ -1,4 +1,4 @@
-name: Lint
+name: Golang Lint
on:
pull_request:
diff --git a/.github/workflows/python_lint.yml b/.github/workflows/python_lint.yml
new file mode 100644
index 00000000..d7e3741d
--- /dev/null
+++ b/.github/workflows/python_lint.yml
@@ -0,0 +1,35 @@
+name: Python Lint
+
+on:
+ pull_request:
+ types: [opened, reopened, synchronize]
+ paths:
+ - 'card_data/**'
+
+permissions:
+ contents: read
+
+jobs:
+ ruff:
+ runs-on: ubuntu-22.04
+ defaults:
+ run:
+ working-directory: card_data
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v6
+
+ - name: Setup Python
+ uses: actions/setup-python@v6
+ with:
+ python-version: '3.12'
+
+ - name: Install uv
+ uses: astral-sh/setup-uv@v7
+
+ - name: Install ruff
+ run: uv tool install ruff
+
+ - name: Lint
+ run: ruff check pipelines/
diff --git a/.github/workflows/python_typing.yml b/.github/workflows/python_typing.yml
new file mode 100644
index 00000000..c84923e1
--- /dev/null
+++ b/.github/workflows/python_typing.yml
@@ -0,0 +1,35 @@
+name: Python Type Check
+
+on:
+ pull_request:
+ types: [opened, reopened, synchronize]
+ paths:
+ - 'card_data/**'
+
+permissions:
+ contents: read
+
+jobs:
+ pyrefly:
+ runs-on: ubuntu-22.04
+ defaults:
+ run:
+ working-directory: card_data
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v6
+
+ - name: Setup Python
+ uses: actions/setup-python@v6
+ with:
+ python-version: '3.12'
+
+ - name: Install uv
+ uses: astral-sh/setup-uv@v7
+
+ - name: Install dependencies
+ run: uv sync --dev
+
+ - name: Type check
+ run: uvx pyrefly check --summarize-errors
\ No newline at end of file
diff --git a/.goreleaser.yml b/.goreleaser.yml
index 17682f04..e2944c1b 100644
--- a/.goreleaser.yml
+++ b/.goreleaser.yml
@@ -14,7 +14,7 @@ builds:
- windows
- darwin
ldflags:
- - -s -w -X main.version=v1.8.8
+ - -s -w -X main.version=v1.8.9
archives:
- formats: [ 'zip' ]
diff --git a/Dockerfile b/Dockerfile
index 5aaa1e38..72616557 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -8,7 +8,7 @@ RUN go mod download
COPY . .
-RUN go build -ldflags "-X main.version=v1.8.8" -o poke-cli .
+RUN go build -ldflags "-X main.version=v1.8.9" -o poke-cli .
# build 2
FROM --platform=$BUILDPLATFORM alpine:3.23
diff --git a/README.md b/README.md
index e1751aa3..bdf0a4e9 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
-
+
@@ -23,6 +23,7 @@ View the [documentation](https://docs.poke-cli.com) on the data infrastructure i
* [Tested Terminals](#tested-terminals)
---
+
## Demo
### Video Game Data
@@ -30,8 +31,10 @@ View the [documentation](https://docs.poke-cli.com) on the data infrastructure i
### Trading Card Game Data
-
+
+
---
+
## Installation
* [Homebrew](#homebrew)
@@ -96,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.8.8 [subcommand] [flag]
+ docker run --rm -it digitalghostdev/poke-cli:v1.8.9 [subcommand] [flag]
```
* Enter the container and use its shell:
```bash
- docker run --rm -it --name poke-cli --entrypoint /bin/sh digitalghostdev/poke-cli:v1.8.8 -c "cd /app && exec sh"
+ docker run --rm -it --name poke-cli --entrypoint /bin/sh digitalghostdev/poke-cli:v1.8.9 -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/definitions.py b/card_data/pipelines/definitions.py
index e1c9c474..e0344d08 100644
--- a/card_data/pipelines/definitions.py
+++ b/card_data/pipelines/definitions.py
@@ -24,8 +24,8 @@ def defs() -> dg.Definitions:
sensors=[discord_success_sensor, discord_failure_sensor],
)
-# Pricing pipeline
-pricing_pipeline_job = dg.define_asset_job(
+# Pricing pipeline job
+pricing_pipeline = dg.define_asset_job(
name="pricing_pipeline_job",
selection=dg.AssetSelection.assets(build_dataframe).downstream(include_self=True),
)
@@ -33,34 +33,34 @@ def defs() -> dg.Definitions:
price_schedule: dg.ScheduleDefinition = dg.ScheduleDefinition(
name="price_schedule",
cron_schedule="0 14 * * *",
- target=pricing_pipeline_job,
+ target=pricing_pipeline,
execution_timezone="America/Los_Angeles",
)
defs_pricing: dg.Definitions = dg.Definitions(
assets=[build_dataframe, load_pricing_data, data_quality_checks_on_pricing],
- jobs=[pricing_pipeline_job],
+ jobs=[pricing_pipeline],
schedules=[price_schedule],
)
-# Series pipeline
-series_pipeline_job = dg.define_asset_job(
+# Series pipeline job
+series_pipeline = dg.define_asset_job(
name="series_pipeline_job",
selection=dg.AssetSelection.assets(extract_series_data).downstream(include_self=True),
)
defs_series: dg.Definitions = dg.Definitions(
assets=[extract_series_data, load_series_data, data_quality_check_on_series],
- jobs=[series_pipeline_job],
+ jobs=[series_pipeline],
)
-# Sets pipeline
-sets_pipeline_job = dg.define_asset_job(
+# Sets pipeline job
+sets_pipeline = dg.define_asset_job(
name="sets_pipeline_job",
selection=dg.AssetSelection.assets(extract_sets_data).downstream(include_self=True),
)
defs_sets: dg.Definitions = dg.Definitions(
assets=[extract_sets_data, load_sets_data, data_quality_check_on_sets],
- jobs=[sets_pipeline_job],
+ jobs=[sets_pipeline],
)
\ No newline at end of file
diff --git a/card_data/pipelines/defs/extract/tcgcsv/extract_pricing.py b/card_data/pipelines/defs/extract/tcgcsv/extract_pricing.py
index 101b6b38..3edf3dc3 100644
--- a/card_data/pipelines/defs/extract/tcgcsv/extract_pricing.py
+++ b/card_data/pipelines/defs/extract/tcgcsv/extract_pricing.py
@@ -10,6 +10,8 @@
SET_PRODUCT_MATCHING = {
+ # Mega Evolution
+ "me02.5": "24541",
"me02": "24448",
"me01": "24380",
# Scarlet & Violet
@@ -57,7 +59,7 @@ class CardPricing(BaseModel):
def is_card(item: dict) -> bool:
- """Check if item has a 'Number' field in extendedData"""
+ """Check if an item has a 'Number' field in extendedData"""
return any(
data_field.get("name") == "Number"
for data_field in item.get("extendedData", [])
@@ -126,15 +128,33 @@ def pull_product_information(set_number: str) -> pl.DataFrame:
if not is_card(card):
continue
- # Skip ball pattern variants (unique to Prismatic Evolutions)
+ # Skip cosmetic holofoil pattern variants.
+ # Prismatic Evolutions (SV08.5) uses Poke Ball / Master Ball patterns.
+ # Ascended Heroes (ME2.5) uses ball-type and energy symbol patterns.
card_name = card.get("name", "")
- if "(Poke Ball Pattern)" in card_name or "(Master Ball Pattern)" in card_name:
+ skip_variants = [
+ "(Poke Ball Pattern)",
+ "(Master Ball Pattern)",
+ "(Love Ball)",
+ "(Energy Symbol Pattern)",
+ "(Poke Ball)",
+ "(Dusk Ball)",
+ "(Quick Ball)",
+ "(Friend Ball)",
+ "(Team Rocket)",
+ "(Exclusive)",
+ ]
+ if any(variant in card_name for variant in skip_variants):
+ continue
+
+ card_number = get_card_number(card)
+ if card_number is None:
continue
card_info = {
"product_id": card["productId"],
"name": extract_card_name(card_name),
- "card_number": get_card_number(card),
+ "card_number": card_number,
"market_price": price_dict.get(card["productId"]),
}
cards_data.append(card_info)
diff --git a/card_data/pipelines/defs/extract/tcgdex/extract_cards.py b/card_data/pipelines/defs/extract/tcgdex/extract_cards.py
index cf6f3505..21fb2cb1 100644
--- a/card_data/pipelines/defs/extract/tcgdex/extract_cards.py
+++ b/card_data/pipelines/defs/extract/tcgdex/extract_cards.py
@@ -10,7 +10,7 @@
@dg.asset(kinds={"API"}, name="extract_card_url_from_set_data")
def extract_card_url_from_set() -> list:
- urls = ["https://api.tcgdex.net/v2/en/sets/me02"]
+ urls = ["https://api.tcgdex.net/v2/en/sets/me02.5"]
all_card_urls = []
diff --git a/card_data/pipelines/defs/extract/tcgdex/extract_sets.py b/card_data/pipelines/defs/extract/tcgdex/extract_sets.py
index 5c635e2a..0de00a26 100644
--- a/card_data/pipelines/defs/extract/tcgdex/extract_sets.py
+++ b/card_data/pipelines/defs/extract/tcgdex/extract_sets.py
@@ -2,7 +2,7 @@
import dagster as dg
import polars as pl
-from pydantic import BaseModel, HttpUrl, ValidationError
+from pydantic import BaseModel, ValidationError
from termcolor import colored
from ....utils.json_retriever import fetch_json
diff --git a/card_data/pipelines/poke_cli_dbt/dbt_project.yml b/card_data/pipelines/poke_cli_dbt/dbt_project.yml
index ee355c7d..ace000b1 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.8.8'
+version: '1.8.9'
profile: 'poke_cli_dbt'
diff --git a/card_data/pipelines/soda/checks_sets.yml b/card_data/pipelines/soda/checks_sets.yml
index 49f330d9..9eab0e7e 100644
--- a/card_data/pipelines/soda/checks_sets.yml
+++ b/card_data/pipelines/soda/checks_sets.yml
@@ -1,6 +1,6 @@
checks for sets:
# Row count validation
- - row_count = 39
+ - row_count = 40
# Schema validation checks
- schema:
diff --git a/card_data/pipelines/tests/json_retriever_test.py b/card_data/pipelines/tests/json_retriever_test.py
new file mode 100644
index 00000000..316da8fc
--- /dev/null
+++ b/card_data/pipelines/tests/json_retriever_test.py
@@ -0,0 +1,142 @@
+import sys
+from pathlib import Path
+
+sys.path.insert(0, str(Path(__file__).parent.parent.parent))
+
+import pytest
+import responses
+import requests
+from pipelines.utils.json_retriever import fetch_json
+
+
+@pytest.mark.benchmark
+@responses.activate
+def test_fetch_json_success():
+ """Test successful JSON retrieval."""
+ responses.add(
+ responses.GET,
+ "https://api.example.com/data",
+ json={"id": 1, "name": "Pikachu"},
+ status=200,
+ )
+
+ result = fetch_json("https://api.example.com/data")
+
+ assert isinstance(result, dict) # nosec
+ assert result["id"] == 1 # nosec
+ assert result["name"] == "Pikachu" # nosec
+
+
+@pytest.mark.benchmark
+@responses.activate
+def test_fetch_json_with_nested_data():
+ """Test retrieval of nested JSON structures."""
+ payload = {
+ "results": [
+ {"productId": 100, "name": "Card A"},
+ {"productId": 200, "name": "Card B"},
+ ],
+ "totalItems": 2,
+ }
+ responses.add(
+ responses.GET,
+ "https://api.example.com/products",
+ json=payload,
+ status=200,
+ )
+
+ result = fetch_json("https://api.example.com/products")
+
+ assert result["totalItems"] == 2 # nosec
+ assert len(result["results"]) == 2 # nosec
+ assert result["results"][0]["productId"] == 100 # nosec
+
+
+@pytest.mark.benchmark
+@responses.activate
+def test_fetch_json_http_404():
+ """Test that a 404 response raises HTTPError."""
+ responses.add(
+ responses.GET,
+ "https://api.example.com/missing",
+ json={"error": "not found"},
+ status=404,
+ )
+
+ with pytest.raises(requests.exceptions.HTTPError):
+ fetch_json("https://api.example.com/missing")
+
+
+@pytest.mark.benchmark
+@responses.activate
+def test_fetch_json_http_500():
+ """Test that a 500 response raises HTTPError."""
+ responses.add(
+ responses.GET,
+ "https://api.example.com/error",
+ json={"error": "internal server error"},
+ status=500,
+ )
+
+ with pytest.raises(requests.exceptions.HTTPError):
+ fetch_json("https://api.example.com/error")
+
+
+@pytest.mark.benchmark
+@responses.activate
+def test_fetch_json_connection_error():
+ """Test that a connection error raises ConnectionError."""
+ responses.add(
+ responses.GET,
+ "https://api.example.com/down",
+ body=requests.exceptions.ConnectionError("Connection refused"),
+ )
+
+ with pytest.raises(requests.exceptions.ConnectionError):
+ fetch_json("https://api.example.com/down")
+
+
+@pytest.mark.benchmark
+@responses.activate
+def test_fetch_json_timeout():
+ """Test that a timeout raises an appropriate exception."""
+ responses.add(
+ responses.GET,
+ "https://api.example.com/slow",
+ body=requests.exceptions.ReadTimeout("Read timed out"),
+ )
+
+ with pytest.raises(requests.exceptions.ReadTimeout):
+ fetch_json("https://api.example.com/slow")
+
+
+@pytest.mark.benchmark
+@responses.activate
+def test_fetch_json_empty_object():
+ """Test retrieval of an empty JSON object."""
+ responses.add(
+ responses.GET,
+ "https://api.example.com/empty",
+ json={},
+ status=200,
+ )
+
+ result = fetch_json("https://api.example.com/empty")
+
+ assert result == {} # nosec
+
+
+@pytest.mark.benchmark
+@responses.activate
+def test_fetch_json_invalid_json():
+ """Test that an invalid JSON body raises a ValueError (JSONDecodeError)."""
+ responses.add(
+ responses.GET,
+ "https://api.example.com/bad",
+ body="not valid json {{{",
+ status=200,
+ content_type="application/json",
+ )
+
+ with pytest.raises(requests.exceptions.JSONDecodeError):
+ fetch_json("https://api.example.com/bad")
diff --git a/card_data/pipelines/tests/secret_retriever_test.py b/card_data/pipelines/tests/secret_retriever_test.py
new file mode 100644
index 00000000..4ba9f92d
--- /dev/null
+++ b/card_data/pipelines/tests/secret_retriever_test.py
@@ -0,0 +1,165 @@
+import sys
+from pathlib import Path
+
+sys.path.insert(0, str(Path(__file__).parent.parent.parent))
+
+import json
+import pytest
+from unittest.mock import patch, MagicMock
+from pipelines.utils.secret_retriever import fetch_secret, fetch_n8n_webhook_secret
+
+@pytest.mark.benchmark
+@patch("pipelines.utils.secret_retriever.SecretCache")
+@patch("pipelines.utils.secret_retriever.botocore.session.get_session")
+def test_fetch_secret_success(mock_get_session, mock_secret_cache_cls):
+ """Test successful retrieval of the Supabase database URI."""
+ secret_payload = json.dumps({"database_uri": "postgresql://user:pass@host/db"})
+
+ mock_cache_instance = MagicMock()
+ mock_cache_instance.get_secret_string.return_value = secret_payload
+ mock_secret_cache_cls.return_value = mock_cache_instance
+
+ result = fetch_secret()
+
+ assert result == "postgresql://user:pass@host/db" # nosec
+ mock_cache_instance.get_secret_string.assert_called_once_with("supabase")
+
+
+@pytest.mark.benchmark
+@patch("pipelines.utils.secret_retriever.SecretCache")
+@patch("pipelines.utils.secret_retriever.botocore.session.get_session")
+def test_fetch_secret_missing_key(mock_get_session, mock_secret_cache_cls):
+ """Test KeyError when the secret JSON is missing 'database_uri'."""
+ secret_payload = json.dumps({"some_other_key": "value"})
+
+ mock_cache_instance = MagicMock()
+ mock_cache_instance.get_secret_string.return_value = secret_payload
+ mock_secret_cache_cls.return_value = mock_cache_instance
+
+ with pytest.raises(KeyError, match="database_uri"):
+ fetch_secret()
+
+
+@pytest.mark.benchmark
+@patch("pipelines.utils.secret_retriever.SecretCache")
+@patch("pipelines.utils.secret_retriever.botocore.session.get_session")
+def test_fetch_secret_invalid_json(mock_get_session, mock_secret_cache_cls):
+ """Test that invalid JSON in the secret raises JSONDecodeError."""
+ mock_cache_instance = MagicMock()
+ mock_cache_instance.get_secret_string.return_value = "not valid json"
+ mock_secret_cache_cls.return_value = mock_cache_instance
+
+ with pytest.raises(json.JSONDecodeError):
+ fetch_secret()
+
+
+@pytest.mark.benchmark
+@patch("pipelines.utils.secret_retriever.SecretCache")
+@patch("pipelines.utils.secret_retriever.botocore.session.get_session")
+def test_fetch_secret_empty_json_object(mock_get_session, mock_secret_cache_cls):
+ """Test KeyError when the secret is an empty JSON object."""
+ mock_cache_instance = MagicMock()
+ mock_cache_instance.get_secret_string.return_value = "{}"
+ mock_secret_cache_cls.return_value = mock_cache_instance
+
+ with pytest.raises(KeyError, match="database_uri"):
+ fetch_secret()
+
+
+@pytest.mark.benchmark
+@patch("pipelines.utils.secret_retriever.SecretCache")
+@patch("pipelines.utils.secret_retriever.botocore.session.get_session")
+def test_fetch_secret_cache_raises(mock_get_session, mock_secret_cache_cls):
+ """Test that an exception from SecretCache propagates."""
+ mock_cache_instance = MagicMock()
+ mock_cache_instance.get_secret_string.side_effect = Exception(
+ "Secret not found"
+ )
+ mock_secret_cache_cls.return_value = mock_cache_instance
+
+ with pytest.raises(Exception, match="Secret not found"):
+ fetch_secret()
+
+
+# ---------------------------------------------------------------------------
+# fetch_n8n_webhook_secret()
+# ---------------------------------------------------------------------------
+
+
+@pytest.mark.benchmark
+@patch("pipelines.utils.secret_retriever.SecretCache")
+@patch("pipelines.utils.secret_retriever.botocore.session.get_session")
+def test_fetch_n8n_webhook_secret_success(mock_get_session, mock_secret_cache_cls):
+ """Test successful retrieval of the n8n webhook URL."""
+ secret_payload = json.dumps({"n8n_webhook": "https://n8n.example.com/hook/abc"})
+
+ mock_cache_instance = MagicMock()
+ mock_cache_instance.get_secret_string.return_value = secret_payload
+ mock_secret_cache_cls.return_value = mock_cache_instance
+
+ result = fetch_n8n_webhook_secret()
+
+ assert result == "https://n8n.example.com/hook/abc" # nosec
+ mock_cache_instance.get_secret_string.assert_called_once_with("n8n_webhook")
+
+
+@pytest.mark.benchmark
+@patch("pipelines.utils.secret_retriever.SecretCache")
+@patch("pipelines.utils.secret_retriever.botocore.session.get_session")
+def test_fetch_n8n_webhook_secret_missing_key(mock_get_session, mock_secret_cache_cls):
+ """Test KeyError when the secret JSON is missing 'n8n_webhook'."""
+ secret_payload = json.dumps({"wrong_key": "value"})
+
+ mock_cache_instance = MagicMock()
+ mock_cache_instance.get_secret_string.return_value = secret_payload
+ mock_secret_cache_cls.return_value = mock_cache_instance
+
+ with pytest.raises(KeyError, match="n8n_webhook"):
+ fetch_n8n_webhook_secret()
+
+
+@pytest.mark.benchmark
+@patch("pipelines.utils.secret_retriever.SecretCache")
+@patch("pipelines.utils.secret_retriever.botocore.session.get_session")
+def test_fetch_n8n_webhook_secret_invalid_json(
+ mock_get_session, mock_secret_cache_cls
+):
+ """Test that invalid JSON in the secret raises JSONDecodeError."""
+ mock_cache_instance = MagicMock()
+ mock_cache_instance.get_secret_string.return_value = "{broken"
+ mock_secret_cache_cls.return_value = mock_cache_instance
+
+ with pytest.raises(json.JSONDecodeError):
+ fetch_n8n_webhook_secret()
+
+
+@pytest.mark.benchmark
+@patch("pipelines.utils.secret_retriever.SecretCache")
+@patch("pipelines.utils.secret_retriever.botocore.session.get_session")
+def test_fetch_n8n_webhook_secret_empty_json_object(
+ mock_get_session, mock_secret_cache_cls
+):
+ """Test KeyError when the secret is an empty JSON object."""
+ mock_cache_instance = MagicMock()
+ mock_cache_instance.get_secret_string.return_value = "{}"
+ mock_secret_cache_cls.return_value = mock_cache_instance
+
+ with pytest.raises(KeyError, match="n8n_webhook"):
+ fetch_n8n_webhook_secret()
+
+
+@pytest.mark.benchmark
+@patch("pipelines.utils.secret_retriever.SecretCache")
+@patch("pipelines.utils.secret_retriever.botocore.session.get_session")
+def test_fetch_n8n_webhook_secret_cache_raises(
+ mock_get_session, mock_secret_cache_cls
+):
+ """Test that an exception from SecretCache propagates."""
+ mock_cache_instance = MagicMock()
+ mock_cache_instance.get_secret_string.side_effect = Exception(
+ "Access denied"
+ )
+ mock_secret_cache_cls.return_value = mock_cache_instance
+
+ with pytest.raises(Exception, match="Access denied"):
+ fetch_n8n_webhook_secret()
diff --git a/card_data/uv.lock b/card_data/uv.lock
index 39d1cf62..6362426a 100644
--- a/card_data/uv.lock
+++ b/card_data/uv.lock
@@ -199,8 +199,8 @@ requires-dist = [
dev = [
{ name = "dagster-dbt", specifier = ">=0.27.3" },
{ name = "dagster-dg-cli" },
- { name = "dagster-postgres", specifier = ">=0.27.3" },
- { name = "dagster-webserver" },
+ { name = "dagster-postgres", specifier = "==0.28.7" },
+ { name = "dagster-webserver", specifier = "==1.12.7" },
{ name = "pytest", specifier = ">=9.0.2" },
{ name = "pytest-codspeed", specifier = ">=4.2.0" },
{ name = "responses", specifier = ">=0.25.8" },
diff --git a/cmd/card/cardlist.go b/cmd/card/cardlist.go
index 09883720..f1683180 100644
--- a/cmd/card/cardlist.go
+++ b/cmd/card/cardlist.go
@@ -295,7 +295,7 @@ func (m CardsModel) View() string {
screen := lipgloss.JoinHorizontal(lipgloss.Top, leftPanel, rightPanel)
return fmt.Sprintf(
- "Highlight a card!\n%s\n%s",
+ "Highlight a card!\n\nNote: Prices are for normal variations of cards.\n%s\n%s",
screen,
styling.KeyMenu.Render("↑ (move up)\n↓ (move down)\n? (view image)\ntab (toggle search)\nctrl+c | esc (quit)"))
}
diff --git a/cmd/card/cardlist_test.go b/cmd/card/cardlist_test.go
index b4dd5cc5..6da89227 100644
--- a/cmd/card/cardlist_test.go
+++ b/cmd/card/cardlist_test.go
@@ -394,4 +394,3 @@ func TestCardDataMsg_EmptyResult(t *testing.T) {
t.Errorf("expected empty maps, got price:%d illus:%d image:%d", len(resultModel.PriceMap), len(resultModel.IllustratorMap), len(resultModel.ImageMap))
}
}
-
diff --git a/cmd/card/imageviewer.go b/cmd/card/imageviewer.go
index 0eaed385..68e96ae5 100644
--- a/cmd/card/imageviewer.go
+++ b/cmd/card/imageviewer.go
@@ -32,7 +32,7 @@ func fetchImageCmd(imageURL string) tea.Cmd {
}
return imageReadyMsg{
imageData: imageData,
- protocol: protocol,
+ protocol: protocol,
}
}
}
diff --git a/cmd/card/setslist.go b/cmd/card/setslist.go
index 69afbaff..2c328a3d 100644
--- a/cmd/card/setslist.go
+++ b/cmd/card/setslist.go
@@ -169,4 +169,3 @@ func SetsList(seriesID string) (SetsModel, error) {
Spinner: s,
}, nil
}
-
diff --git a/cmd/move/move.go b/cmd/move/move.go
index 0ddd236c..37db39ec 100644
--- a/cmd/move/move.go
+++ b/cmd/move/move.go
@@ -97,7 +97,7 @@ func moveInfoContainer(output *strings.Builder, moveStruct structs.MoveJSONStruc
func moveEffectContainer(output *strings.Builder, moveStruct structs.MoveJSONStruct) {
var sv string
- var swsh string
+ var swsh string
docStyle := lipgloss.NewStyle().
Padding(1, 2).
diff --git a/connections/connection.go b/connections/connection.go
index 3c35ffb8..9a3242de 100644
--- a/connections/connection.go
+++ b/connections/connection.go
@@ -130,4 +130,4 @@ func CallTCGData(url string) ([]byte, error) {
}
return body, nil
-}
\ No newline at end of file
+}
diff --git a/nfpm.yaml b/nfpm.yaml
index e2fded10..32cf979d 100644
--- a/nfpm.yaml
+++ b/nfpm.yaml
@@ -1,7 +1,7 @@
name: "poke-cli"
arch: "arm64"
platform: "linux"
-version: "v1.8.8"
+version: "v1.8.9"
section: "default"
version_schema: semver
maintainer: "Christian S"
diff --git a/testdata/main_latest_flag.golden b/testdata/main_latest_flag.golden
index 306c19b5..1b82be83 100644
--- a/testdata/main_latest_flag.golden
+++ b/testdata/main_latest_flag.golden
@@ -2,6 +2,6 @@
┃ ┃
┃ Latest available release ┃
┃ on GitHub: ┃
-┃ • v1.8.7 ┃
+┃ • v1.8.8 ┃
┃ ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛