gh-space-shooter 0.0.4__tar.gz → 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/.github/workflows/publish.yml +6 -5
  2. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/.github/workflows/test.yml +2 -2
  3. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/.gitignore +2 -1
  4. gh_space_shooter-1.0.0/.python-version +1 -0
  5. gh_space_shooter-1.0.0/CLAUDE.md +90 -0
  6. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/PKG-INFO +11 -6
  7. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/README.md +9 -4
  8. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/action.yml +1 -1
  9. gh_space_shooter-1.0.0/app/README.md +53 -0
  10. gh_space_shooter-1.0.0/app/public/favicon.ico +0 -0
  11. gh_space_shooter-1.0.0/app/pyproject.toml +19 -0
  12. gh_space_shooter-1.0.0/app/src/main.py +78 -0
  13. gh_space_shooter-1.0.0/app/src/templates/index.html +507 -0
  14. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/pyproject.toml +9 -3
  15. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/cli.py +25 -4
  16. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/animator.py +18 -8
  17. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/renderer.py +31 -5
  18. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/github_client.py +1 -1
  19. gh_space_shooter-1.0.0/src/gh_space_shooter/py.typed +0 -0
  20. gh_space_shooter-1.0.0/uv.lock +1093 -0
  21. gh_space_shooter-0.0.4/.python-version +0 -1
  22. gh_space_shooter-0.0.4/uv.lock +0 -305
  23. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/.github/dependabot.yml +0 -0
  24. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/LICENSE +0 -0
  25. /gh_space_shooter-0.0.4/src/gh_space_shooter/py.typed → /gh_space_shooter-1.0.0/app/src/__init__.py +0 -0
  26. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/example.gif +0 -0
  27. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/__init__.py +0 -0
  28. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/console_printer.py +0 -0
  29. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/constants.py +0 -0
  30. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/__init__.py +0 -0
  31. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/drawables/__init__.py +0 -0
  32. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/drawables/bullet.py +0 -0
  33. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/drawables/drawable.py +0 -0
  34. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/drawables/enemy.py +0 -0
  35. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/drawables/explosion.py +0 -0
  36. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/drawables/ship.py +0 -0
  37. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/drawables/starfield.py +0 -0
  38. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/game_state.py +0 -0
  39. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/render_context.py +0 -0
  40. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/strategies/__init__.py +0 -0
  41. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/strategies/base_strategy.py +0 -0
  42. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/strategies/column_strategy.py +0 -0
  43. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/strategies/random_strategy.py +0 -0
  44. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/strategies/row_strategy.py +0 -0
  45. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/tests/conftest.py +0 -0
  46. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/tests/test_bullet_collision.py +0 -0
  47. {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/tests/test_strategies.py +0 -0
@@ -14,7 +14,7 @@ on:
14
14
  - patch
15
15
 
16
16
  env:
17
- PYTHON_LATEST: 3.13
17
+ PYTHON_LATEST: 3.12
18
18
 
19
19
  jobs:
20
20
  build:
@@ -45,11 +45,12 @@ jobs:
45
45
  - name: Publish
46
46
  run: uv publish --token ${{ secrets.PYPI_API_TOKEN }}
47
47
 
48
- - name: GitHub Release v${{ steps.bump.outputs.current-version }}
48
+ - name: GitHub Release PIP v${{ steps.bump.outputs.current-version }}
49
49
  uses: ncipollo/release-action@v1
50
50
  with:
51
- name: gh-space-shooter v${{ steps.bump.outputs.current-version }}
51
+ name: gh-space-shooter pypi v${{ steps.bump.outputs.current-version }}
52
52
  tag: v${{ steps.bump.outputs.current-version }}
53
- body: "Automated release of version v${{ steps.bump.outputs.current-version }}"
53
+ body: "Automated release of pypi version v${{ steps.bump.outputs.current-version }}"
54
54
  artifacts: dist/*
55
- token: ${{ secrets.GH_TOKEN }}
55
+ token: ${{ secrets.GH_TOKEN }}
56
+ generateReleaseNotes: true
@@ -7,7 +7,7 @@ on:
7
7
  types: [opened, synchronize, reopened]
8
8
 
9
9
  env:
10
- PYTHON_LATEST: 3.13
10
+ PYTHON_LATEST: 3.12
11
11
 
12
12
  jobs:
13
13
  test:
@@ -15,7 +15,7 @@ jobs:
15
15
  runs-on: ubuntu-latest
16
16
  strategy:
17
17
  matrix:
18
- python-version: ["3.13"]
18
+ python-version: ["3.12"]
19
19
 
20
20
  steps:
21
21
  - name: Checkout code
@@ -19,4 +19,5 @@ wheels/
19
19
  .env.*
20
20
 
21
21
  # Test output
22
- my-contributions.json
22
+ my-contributions.json
23
+ .vercel
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,90 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Build and Development Commands
6
+
7
+ ```bash
8
+ # Install dependencies (uses uv package manager)
9
+ uv sync
10
+
11
+ # Install with dev dependencies
12
+ uv sync --extra dev
13
+
14
+ # Run the CLI
15
+ uv run gh-space-shooter <username>
16
+
17
+ # Run tests
18
+ uv run pytest tests/ -v
19
+
20
+ # Run a single test file
21
+ uv run pytest tests/test_strategies.py -v
22
+
23
+ # Run a specific test
24
+ uv run pytest tests/test_strategies.py::test_column_strategy -v
25
+
26
+ # Run the web app (from app/ directory)
27
+ uv run --project gh-space-shooter-app uvicorn main:app
28
+ ```
29
+
30
+ ## Environment Setup
31
+
32
+ Requires a GitHub Personal Access Token with `read:user` scope:
33
+ ```bash
34
+ export GH_TOKEN=your_token_here
35
+ # Or create a .env file with GH_TOKEN=your_token
36
+ ```
37
+
38
+ ## Project Context
39
+
40
+ **Current main usage**: GitHub Action that automatically updates a game GIF in user repositories daily (see `.github/workflows/` for the action definition).
41
+
42
+ **Web App**: A FastAPI-based web application is available in the `app/` directory for on-demand GIF generation. See `app/README.md` for details.
43
+
44
+ ## Architecture Overview
45
+
46
+ This is a CLI tool that transforms GitHub contribution graphs into animated space shooter GIFs using Pillow.
47
+
48
+ ### Core Flow
49
+
50
+ 1. **CLI (`cli.py`)** - Typer-based entry point that orchestrates the pipeline
51
+ 2. **GitHubClient (`github_client.py`)** - Fetches contribution data via GitHub GraphQL API, returns typed `ContributionData` dict
52
+ 3. **Animator (`game/animator.py`)** - Main game loop that coordinates strategy execution and frame generation
53
+ 4. **GameState (`game/game_state.py`)** - Central state container holding ship, enemies, bullets, explosions
54
+ 5. **Renderer (`game/renderer.py`)** - Converts GameState to PIL Images each frame
55
+
56
+ ### Strategy Pattern
57
+
58
+ Strategies (`game/strategies/`) define how the ship clears enemies:
59
+ - `BaseStrategy` - Abstract base defining `generate_actions(game_state) -> Iterator[Action]`
60
+ - `ColumnStrategy` - Clears enemies column by column (left to right)
61
+ - `RowStrategy` - Clears enemies row by row (top to bottom)
62
+ - `RandomStrategy` - Targets enemies in random order
63
+
64
+ Strategies yield `Action(x, shoot)` objects. The Animator processes these: moving the ship to position `x`, waiting for movement/cooldown to complete, then shooting if `shoot=True`.
65
+
66
+ ### Drawable System
67
+
68
+ All game objects inherit from `Drawable` (`game/drawables/drawable.py`):
69
+ - `animate(delta_time)` - Update state (position, cooldowns, particles)
70
+ - `draw(draw, context)` - Render to PIL ImageDraw
71
+
72
+ Drawables: `Ship`, `Enemy`, `Bullet`, `Explosion`, `Starfield`
73
+
74
+ The `RenderContext` (`game/render_context.py`) holds theming (colors, cell sizes, padding) and coordinate conversion helpers.
75
+
76
+ ### Animation Loop
77
+
78
+ In `Animator._generate_frames()`:
79
+ 1. Strategy yields next action
80
+ 2. Ship moves to target x position (animate frames until arrived)
81
+ 3. Ship shoots if action.shoot (animate frames for bullet travel + explosions)
82
+ 4. Repeat until all enemies destroyed
83
+
84
+ Frame rate is configurable (default 40 FPS). All speeds use delta_time for frame-rate independence.
85
+
86
+ ### Key Constants (`constants.py`)
87
+
88
+ - `NUM_WEEKS = 52` - Contribution graph width
89
+ - `NUM_DAYS = 7` - Contribution graph height
90
+ - Speeds are in cells/second, durations in seconds
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gh-space-shooter
3
- Version: 0.0.4
3
+ Version: 1.0.0
4
4
  Summary: A CLI tool that visualizes GitHub contribution graphs as gamified GIFs
5
5
  Author-email: zane <czl970721@gmail.com>
6
6
  License-File: LICENSE
7
- Requires-Python: >=3.13
7
+ Requires-Python: >=3.12
8
8
  Requires-Dist: httpx>=0.27.0
9
9
  Requires-Dist: pillow>=10.1.0
10
10
  Requires-Dist: python-dotenv>=1.0.0
@@ -22,6 +22,10 @@ Transform your GitHub contribution graph into an epic space shooter game!
22
22
 
23
23
  ## Usage
24
24
 
25
+ ### Onetime Generation
26
+
27
+ A [web interface](https://gh-space-shooter.kiyo-n-zane.com) is available for on-demand GIF generation without installing anything locally.
28
+
25
29
  ### GitHub Action
26
30
 
27
31
  Automatically update your game GIF daily using GitHub Actions! Add this workflow to your repository at `.github/workflows/update-game.yml`:
@@ -122,14 +126,15 @@ gh-space-shooter torvalds --output my-epic-game.gif
122
126
  gh-space-shooter torvalds -o my-game.gif
123
127
 
124
128
  # Choose enemy attack strategy
125
- gh-space-shooter torvalds --strategy column # Enemies attack in columns
126
129
  gh-space-shooter torvalds --strategy row # Enemies attack in rows
127
130
  gh-space-shooter torvalds -s random # Random chaos (default)
128
131
 
129
132
  # Adjust animation frame rate
130
- gh-space-shooter torvalds --fps 25 # Slower, smaller file size
131
- gh-space-shooter torvalds --fps 40 # Default frame rate
132
- gh-space-shooter torvalds --fps 50 # Smoother animation
133
+ gh-space-shooter torvalds --fps 25 # Lower Frame rate, Smaller file size
134
+ gh-space-shooter torvalds --fps 40 # Default Frame rate, Larger file size
135
+
136
+ # Stop the animation earlier
137
+ gh-space-shooter torvalds --max-frame 200 # Stop after 200 frames
133
138
  ```
134
139
 
135
140
  This creates an animated GIF showing:
@@ -6,6 +6,10 @@ Transform your GitHub contribution graph into an epic space shooter game!
6
6
 
7
7
  ## Usage
8
8
 
9
+ ### Onetime Generation
10
+
11
+ A [web interface](https://gh-space-shooter.kiyo-n-zane.com) is available for on-demand GIF generation without installing anything locally.
12
+
9
13
  ### GitHub Action
10
14
 
11
15
  Automatically update your game GIF daily using GitHub Actions! Add this workflow to your repository at `.github/workflows/update-game.yml`:
@@ -106,14 +110,15 @@ gh-space-shooter torvalds --output my-epic-game.gif
106
110
  gh-space-shooter torvalds -o my-game.gif
107
111
 
108
112
  # Choose enemy attack strategy
109
- gh-space-shooter torvalds --strategy column # Enemies attack in columns
110
113
  gh-space-shooter torvalds --strategy row # Enemies attack in rows
111
114
  gh-space-shooter torvalds -s random # Random chaos (default)
112
115
 
113
116
  # Adjust animation frame rate
114
- gh-space-shooter torvalds --fps 25 # Slower, smaller file size
115
- gh-space-shooter torvalds --fps 40 # Default frame rate
116
- gh-space-shooter torvalds --fps 50 # Smoother animation
117
+ gh-space-shooter torvalds --fps 25 # Lower Frame rate, Smaller file size
118
+ gh-space-shooter torvalds --fps 40 # Default Frame rate, Larger file size
119
+
120
+ # Stop the animation earlier
121
+ gh-space-shooter torvalds --max-frame 200 # Stop after 200 frames
117
122
  ```
118
123
 
119
124
  This creates an animated GIF showing:
@@ -42,7 +42,7 @@ runs:
42
42
  - name: Set up Python
43
43
  uses: actions/setup-python@v5
44
44
  with:
45
- python-version: '3.13'
45
+ python-version: '3.12'
46
46
 
47
47
  - name: Install gh-space-shooter
48
48
  shell: bash
@@ -0,0 +1,53 @@
1
+ # gh-space-shooter Web App
2
+
3
+ A FastAPI web application that provides on-demand GitHub Space Shooter GIF generation through a browser interface.
4
+
5
+ ## Features
6
+
7
+ - Web UI for generating GIFs without CLI installation
8
+ - Select GitHub username and animation strategy (random, column, row)
9
+ - Download generated GIFs directly
10
+ - Share functionality for supported browsers
11
+
12
+ ## Setup
13
+
14
+ 1. Install dependencies:
15
+ ```bash
16
+ cd app
17
+ uv sync
18
+ ```
19
+
20
+ 2. Set up environment:
21
+ ```bash
22
+ export GH_TOKEN=your_github_token
23
+ # Or create a .env file with GH_TOKEN=your_token
24
+ ```
25
+
26
+ ## Running Locally
27
+
28
+ ```bash
29
+ uv run --project gh-space-shooter-app uvicorn main:app
30
+ ```
31
+
32
+ The app will be available at `http://localhost:8000`.
33
+
34
+ ## API Endpoints
35
+
36
+ - `GET /` - Web UI for generating GIFs
37
+ - `GET /api/generate?username=<username>&strategy=<strategy>` - Generate and return a GIF
38
+ - `username` (required): GitHub username
39
+ - `strategy` (optional): Animation strategy - `random`, `column`, or `row` (default: `random`)
40
+
41
+ ## Project Structure
42
+
43
+ ```
44
+ app/
45
+ ├── src/
46
+ │ ├── main.py # FastAPI application
47
+ │ └── templates/
48
+ │ └── index.html # Web UI template
49
+ ├── public/
50
+ │ └── favicon.ico
51
+ ├── pyproject.toml
52
+ └── README.md
53
+ ```
@@ -0,0 +1,19 @@
1
+ [project]
2
+ name = "gh-space-shooter-app"
3
+ version = "0.1.0"
4
+ description = "Web app wrapper for gh-space-shooter GIF generation"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ dependencies = [
8
+ "gh-space-shooter",
9
+ "fastapi[standard]",
10
+ "uvicorn[standard]",
11
+ "jinja2",
12
+ ]
13
+
14
+ [build-system]
15
+ requires = ["setuptools"]
16
+ build-backend = "setuptools.build_meta"
17
+
18
+ [project.scripts]
19
+ app = "main:app"
@@ -0,0 +1,78 @@
1
+ """FastAPI web app for gh-space-shooter GIF generation."""
2
+
3
+ import os
4
+ from io import BytesIO
5
+ from pathlib import Path
6
+
7
+ from dotenv import load_dotenv
8
+ from fastapi import FastAPI, HTTPException, Query
9
+ from fastapi.requests import Request
10
+ from fastapi.responses import HTMLResponse, Response
11
+ # from fastapi.staticfiles import StaticFiles
12
+ from fastapi.templating import Jinja2Templates
13
+
14
+ from gh_space_shooter.game import Animator, ColumnStrategy, RandomStrategy, RowStrategy, BaseStrategy
15
+ from gh_space_shooter.github_client import GitHubAPIError, GitHubClient
16
+
17
+ load_dotenv()
18
+
19
+ app = FastAPI(title="GitHub Space Shooter")
20
+
21
+ templates = Jinja2Templates(directory=Path(__file__).parent / "templates")
22
+ # app.mount("/public", StaticFiles(directory=Path(__file__).parent / "public"), name="public")
23
+
24
+
25
+ STRATEGY_MAP: dict[str, type[BaseStrategy]] = {
26
+ "column": ColumnStrategy,
27
+ "row": RowStrategy,
28
+ "random": RandomStrategy,
29
+ }
30
+
31
+
32
+ def generate_gif(username: str, strategy: str, token: str) -> BytesIO:
33
+ """Generate a space shooter GIF for a GitHub user."""
34
+ with GitHubClient(token) as client:
35
+ data = client.get_contribution_graph(username)
36
+
37
+ strategy_class: type[BaseStrategy] = STRATEGY_MAP.get(strategy, RandomStrategy)
38
+ strat = strategy_class()
39
+
40
+ animator = Animator(data, strat, fps=20, watermark=True)
41
+ return animator.generate_gif(maxFrame=250)
42
+
43
+ @app.get("/", response_class=HTMLResponse)
44
+ async def index(request: Request):
45
+ """Serve the main page."""
46
+ return templates.TemplateResponse(request, "index.html")
47
+
48
+
49
+ @app.get("/api/generate")
50
+ async def generate(
51
+ username: str = Query(..., min_length=1, description="GitHub username"),
52
+ strategy: str = Query("random", description="Animation strategy"),
53
+ ):
54
+ """Generate and return a space shooter GIF."""
55
+ token = os.getenv("GH_TOKEN")
56
+ if not token:
57
+ raise HTTPException(status_code=500, detail="GitHub token not configured")
58
+
59
+ if strategy not in STRATEGY_MAP:
60
+ raise HTTPException(
61
+ status_code=400,
62
+ detail=f"Invalid strategy. Choose from: {', '.join(STRATEGY_MAP.keys())}",
63
+ )
64
+
65
+ try:
66
+ gif_buffer = generate_gif(username, strategy, token)
67
+ return Response(
68
+ content=gif_buffer.getvalue(),
69
+ media_type="image/gif",
70
+ headers={
71
+ "Response-Type": "blob",
72
+ "Content-Disposition": f"inline; filename={username}-space-shooter.gif"
73
+ },
74
+ )
75
+ except GitHubAPIError as e:
76
+ raise HTTPException(status_code=400, detail=str(e))
77
+ except Exception as e:
78
+ raise HTTPException(status_code=500, detail=f"Failed to generate GIF: {e}")