Skip to content

Commit 28dbf69

Browse files
Merge pull request #298 from digitalghost-dev/1.10.3
1.10.3
2 parents f80ee7a + 12f5e3b commit 28dbf69

39 files changed

Lines changed: 445 additions & 246 deletions

.github/workflows/ci.yml

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ on:
3131
- main
3232

3333
env:
34-
VERSION_NUMBER: 'v1.10.2'
34+
VERSION_NUMBER: 'v1.10.3'
3535
DOCKERHUB_REGISTRY_NAME: 'digitalghostdev/poke-cli'
3636
AWS_REGION: 'us-west-2'
3737

@@ -59,10 +59,38 @@ jobs:
5959
with:
6060
sarif_file: results.sarif
6161

62+
bandit:
63+
runs-on: ubuntu-22.04
64+
65+
steps:
66+
- name: Checkout
67+
uses: actions/checkout@v6
68+
69+
- name: Set up Python
70+
uses: actions/setup-python@v6
71+
with:
72+
python-version: '3.12'
73+
74+
- name: Install uv
75+
uses: astral-sh/setup-uv@v7
76+
77+
- name: Run Bandit Security Scanner
78+
run: |
79+
uv tool run --from 'bandit[sarif,toml]' bandit \
80+
-r card_data/pipelines \
81+
-f sarif \
82+
-o bandit-results.sarif \
83+
|| true
84+
85+
- name: Upload SARIF Report
86+
uses: github/codeql-action/upload-sarif@v4
87+
with:
88+
sarif_file: bandit-results.sarif
89+
6290
gitleaks:
6391
runs-on: ubuntu-22.04
64-
needs: [gosec]
65-
if: needs.gosec.result == 'success'
92+
needs: [gosec, bandit]
93+
if: needs.gosec.result == 'success' && needs.bandit.result == 'success'
6694

6795
steps:
6896
- name: Checkout
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
name: Python Tests
22

33
on:
4+
push:
5+
branches:
6+
- main
7+
paths:
8+
- 'card_data/**'
9+
- 'web/**'
410
pull_request:
511
types: [opened, reopened, synchronize]
612
paths:

.goreleaser.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ builds:
1414
- windows
1515
- darwin
1616
ldflags:
17-
- -s -w -X main.version=v1.10.2
17+
- -s -w -X main.version=v1.10.3
1818

1919
archives:
2020
- formats: [ 'zip' ]

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# build 1
2-
FROM golang:1.25.9-alpine3.23 AS build
2+
FROM golang:1.25.10-alpine3.23 AS build
33

44
WORKDIR /app
55

@@ -8,7 +8,7 @@ RUN go mod download
88

99
COPY . .
1010

11-
RUN go build -ldflags "-X main.version=v1.10.2" -o poke-cli .
11+
RUN go build -ldflags "-X main.version=v1.10.3" -o poke-cli .
1212

1313
# build 2
1414
FROM --platform=$BUILDPLATFORM alpine:3.23

README.md

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<img width="425" src="poke-cli.png" alt="pokemon-logo"/>
33
<h4></h4>
44
<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">
5-
<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">
5+
<img src="https://img.shields.io/docker/image-size/digitalghostdev/poke-cli/v1.10.3?arch=arm64&style=flat-square&logo=docker&logoColor=FFCC00&labelColor=EEE&color=FFCC00" alt="docker-image-size">
66
<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">
77
</div>
88
<div align="center">
@@ -22,6 +22,9 @@ View the [documentation](https://docs.poke-cli.com) on the data infrastructure i
2222
* [Roadmap](#roadmap)
2323
* [Tested Terminals](#tested-terminals)
2424

25+
> ![NOTE]
26+
> A version 2 is being planned and built that will remove/update some commands and flags. Refer to the changes under the [Roadmap](#version-2-changes) for more information.
27+
2528
---
2629

2730
## Demo
@@ -99,11 +102,11 @@ Cloudsmith is a fully cloud-based service that lets you easily create, store, an
99102
3. Choose how to interact with the container:
100103
* Run a single command and exit:
101104
```bash
102-
docker run --rm -it digitalghostdev/poke-cli:v1.10.2 <command> [subcommand] [flag]
105+
docker run --rm -it digitalghostdev/poke-cli:v1.10.3 <command> [subcommand] [flag]
103106
```
104107
* Enter the container and use its shell:
105108
```bash
106-
docker run --rm -it --name poke-cli --entrypoint /bin/sh digitalghostdev/poke-cli:v1.10.2 -c "cd /app && exec sh"
109+
docker run --rm -it --name poke-cli --entrypoint /bin/sh digitalghostdev/poke-cli:v1.10.3 -c "cd /app && exec sh"
107110
# placed into the /app directory, run the program with './poke-cli'
108111
# example: ./poke-cli ability swift-swim
109112
```
@@ -112,13 +115,13 @@ Cloudsmith is a fully cloud-based service that lets you easily create, store, an
112115
> 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:
113116
> ```bash
114117
> # Kitty
115-
> docker run --rm -it -e TERM -e KITTY_WINDOW_ID digitalghostdev/poke-cli:v1.10.2 card
118+
> docker run --rm -it -e TERM -e KITTY_WINDOW_ID digitalghostdev/poke-cli:v1.10.3 card
116119
>
117120
> # WezTerm, iTerm2, Ghostty, Konsole, Rio, Tabby
118-
> docker run --rm -it -e TERM -e TERM_PROGRAM digitalghostdev/poke-cli:v1.10.2 card
121+
> docker run --rm -it -e TERM -e TERM_PROGRAM digitalghostdev/poke-cli:v1.10.3 card
119122
>
120123
> # Windows Terminal (Sixel)
121-
> docker run --rm -it -e WT_SESSION digitalghostdev/poke-cli:v1.10.2 card
124+
> docker run --rm -it -e WT_SESSION digitalghostdev/poke-cli:v1.10.3 card
122125
> ```
123126
> If your terminal is not listed above, image rendering is not supported inside Docker.
124127

@@ -235,6 +238,16 @@ Below is a list of the planned/completed commands and flags:
235238
- [x] `tcg`: get data about TCG tournaments.
236239
- [x] `types`: get data about a specific typing.
237240
241+
### Version 2 Changes
242+
The following planned changes in `v2`:
243+
244+
- `pokemon <name> -t | --types` — removed; typing is included by default.
245+
- `pokemon <name> --defense` - being renamed to `--defenses` to keep consistency with other flags in the `pokemon` command.
246+
- `natures` — moves to a flag under a new `mechanics` command.
247+
- `tcg` — moves to a new `comp` command (covers competitive TCG *and* VGC data).
248+
- Adding `pflag` library to enforce POSIX style flags.
249+
250+
238251
---
239252
## Tested Terminals
240253
| Terminal | OS | Status | Issues |

card_data/pipelines/definitions.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99
from .defs.extract.tcgdex.extract_sets import extract_sets_data
1010
from .defs.extract.tcgdex.extract_series import extract_series_data
1111
from .defs.load.limitless.load_standings import load_standings_data
12-
from .defs.load.tcgcsv.load_pricing import load_pricing_data, data_quality_checks_on_pricing
12+
from .defs.load.tcgcsv.load_pricing import (
13+
load_pricing_data,
14+
data_quality_checks_on_pricing,
15+
)
1316
from .defs.load.tcgdex.load_sets import load_sets_data, data_quality_check_on_sets
1417
from .defs.load.tcgdex.load_series import load_series_data, data_quality_check_on_series
1518
from .sensors import discord_success_sensor, discord_failure_sensor
@@ -18,7 +21,9 @@
1821
@definitions
1922
def defs() -> dg.Definitions:
2023
# load_from_defs_folder discovers dbt assets from transform_data.py
21-
folder_defs: dg.Definitions = load_from_defs_folder(project_root=Path(__file__).parent.parent)
24+
folder_defs: dg.Definitions = load_from_defs_folder(
25+
project_root=Path(__file__).parent.parent
26+
)
2227
return dg.Definitions.merge(
2328
folder_defs,
2429
defs_discord_sensors,
@@ -56,7 +61,9 @@ def defs() -> dg.Definitions:
5661
# Series pipeline job
5762
series_pipeline = dg.define_asset_job(
5863
name="series_pipeline_job",
59-
selection=dg.AssetSelection.assets(extract_series_data).downstream(include_self=True),
64+
selection=dg.AssetSelection.assets(extract_series_data).downstream(
65+
include_self=True
66+
),
6067
)
6168

6269
defs_series: dg.Definitions = dg.Definitions(
@@ -78,7 +85,9 @@ def defs() -> dg.Definitions:
7885
# Standings pipeline job
7986
standings_pipeline = dg.define_asset_job(
8087
name="standings_pipeline_job",
81-
selection=dg.AssetSelection.assets(create_standings_dataframe).downstream(include_self=True),
88+
selection=dg.AssetSelection.assets(create_standings_dataframe).downstream(
89+
include_self=True
90+
),
8291
)
8392

8493
defs_standings: dg.Definitions = dg.Definitions(
@@ -94,4 +103,4 @@ def defs() -> dg.Definitions:
94103

95104
defs_champions_speed_tiers: dg.Definitions = dg.Definitions(
96105
jobs=[champions_speed_tiers_pipeline],
97-
)
106+
)

card_data/pipelines/defs/extract/limitless/extract_standings.py

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
import requests
44
from bs4 import BeautifulSoup, Tag
55

6+
67
def seasons(year: int) -> list[dict]:
78
url = "https://labs.limitlesstcg.com/"
89
r = requests.get(url)
9-
soup = BeautifulSoup(r.content, 'html.parser')
10+
soup = BeautifulSoup(r.content, "html.parser")
1011

1112
season_header = soup.find("h2", string=lambda x: bool(x and f"{year}" in x))
1213
if not isinstance(season_header, Tag):
@@ -20,11 +21,13 @@ def seasons(year: int) -> list[dict]:
2021
if not isinstance(a, Tag):
2122
continue
2223
parts = list(a.stripped_strings)
23-
tournaments.append({
24-
"name": parts[0],
25-
"date": parts[-1],
26-
"link": f"https://labs.limitlesstcg.com{a['href']}",
27-
})
24+
tournaments.append(
25+
{
26+
"name": parts[0],
27+
"date": parts[-1],
28+
"link": f"https://labs.limitlesstcg.com{a['href']}",
29+
}
30+
)
2831

2932
return tournaments
3033

@@ -33,22 +36,33 @@ def build_standings(tournament: dict) -> pl.DataFrame | None:
3336
tournament_id = tournament["link"].split("/")[-2]
3437

3538
r = requests.get(tournament["link"])
36-
soup = BeautifulSoup(r.content, 'html.parser')
39+
soup = BeautifulSoup(r.content, "html.parser")
3740

3841
table = soup.find("table", class_="data-table striped")
3942

4043
if isinstance(table, Tag):
41-
headers = ['Rank', 'Name', 'Country', 'Points', 'Record', 'OPW%', 'OOPW%', 'Deck', 'Decklist', 'Unknown']
44+
headers = [
45+
"Rank",
46+
"Name",
47+
"Country",
48+
"Points",
49+
"Record",
50+
"OPW%",
51+
"OOPW%",
52+
"Deck",
53+
"Decklist",
54+
"Unknown",
55+
]
4256

4357
rows = []
44-
tbody = table.find('tbody')
58+
tbody = table.find("tbody")
4559
if not isinstance(tbody, Tag):
4660
return None
4761

48-
for tr in tbody.find_all('tr'):
62+
for tr in tbody.find_all("tr"):
4963
if not isinstance(tr, Tag):
5064
continue
51-
cells = tr.find_all('td')
65+
cells = tr.find_all("td")
5266

5367
if len(cells) == 1:
5468
continue
@@ -58,29 +72,37 @@ def build_standings(tournament: dict) -> pl.DataFrame | None:
5872
if not isinstance(td, Tag):
5973
continue
6074
if i == 2:
61-
img = td.find('img')
75+
img = td.find("img")
6276
if isinstance(img, Tag):
63-
country = img.get('alt') or img.get('title') or ''
77+
country = img.get("alt") or img.get("title") or ""
6478
row_data.append(country)
6579
else:
66-
row_data.append('')
80+
row_data.append("")
6781

6882
elif i == 7: # Deck column
69-
pokemon_imgs = td.find_all('img', class_='pokemon')
83+
pokemon_imgs = td.find_all("img", class_="pokemon")
7084
if pokemon_imgs:
71-
pokemon_names = [str(img.get('alt', '')) for img in pokemon_imgs if isinstance(img, Tag) and img.get('alt')]
72-
pokemon_string = '/'.join(pokemon_names)
85+
pokemon_names = [
86+
str(img.get("alt", ""))
87+
for img in pokemon_imgs
88+
if isinstance(img, Tag) and img.get("alt")
89+
]
90+
pokemon_string = "/".join(pokemon_names)
7391
row_data.append(pokemon_string)
7492
else:
75-
row_data.append('')
93+
row_data.append("")
7694

7795
elif i == 8: # Decklist column
78-
link = td.find('a')
96+
link = td.find("a")
7997
if isinstance(link, Tag):
80-
decklist_url = link.get('href', '')
81-
row_data.append(f"https://labs.limitlesstcg.com{decklist_url}" if decklist_url else '')
98+
decklist_url = link.get("href", "")
99+
row_data.append(
100+
f"https://labs.limitlesstcg.com{decklist_url}"
101+
if decklist_url
102+
else ""
103+
)
82104
else:
83-
row_data.append('')
105+
row_data.append("")
84106

85107
else:
86108
cell_text = td.get_text(strip=True)

card_data/pipelines/defs/extract/tcgcsv/extract_pricing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
"sm3.5": "2054",
6666
"sm3": "1957",
6767
"sm2": "1919",
68-
"sm1": "1863"
68+
"sm1": "1863",
6969
}
7070

7171

card_data/pipelines/defs/extract/tcgdex/extract_series.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ def extract_series_data() -> pl.DataFrame:
3131
raise
3232

3333
filtered = [
34-
s.model_dump(mode="json") for s in validated if s.id in ["me", "sv", "swsh", "sm"]
34+
s.model_dump(mode="json")
35+
for s in validated
36+
if s.id in ["me", "sv", "swsh", "sm"]
3537
]
3638
return pl.DataFrame(filtered)

card_data/pipelines/defs/load/tcgcsv/load_pricing.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import subprocess # nosec
1+
import subprocess # nosec
22
from pathlib import Path
33

44
import dagster as dg
@@ -37,7 +37,7 @@ def data_quality_checks_on_pricing() -> None:
3737
current_file_dir = Path(__file__).parent
3838
print(f"Setting cwd to: {current_file_dir}")
3939

40-
result = subprocess.run( # nosec
40+
result = subprocess.run( # nosec
4141
[
4242
"soda",
4343
"scan",
@@ -58,4 +58,6 @@ def data_quality_checks_on_pricing() -> None:
5858
print(result.stderr)
5959

6060
if result.returncode != 0:
61-
raise Exception(f"Soda data quality checks failed with return code {result.returncode}")
61+
raise Exception(
62+
f"Soda data quality checks failed with return code {result.returncode}"
63+
)

0 commit comments

Comments
 (0)