gh-space-shooter 0.0.5__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.
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/.github/workflows/publish.yml +1 -1
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/.github/workflows/test.yml +2 -2
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/.gitignore +2 -1
- gh_space_shooter-1.0.0/.python-version +1 -0
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/CLAUDE.md +4 -1
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/PKG-INFO +6 -2
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/README.md +4 -0
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/action.yml +1 -1
- gh_space_shooter-1.0.0/app/README.md +53 -0
- gh_space_shooter-1.0.0/app/public/favicon.ico +0 -0
- gh_space_shooter-1.0.0/app/pyproject.toml +19 -0
- gh_space_shooter-1.0.0/app/src/main.py +78 -0
- gh_space_shooter-1.0.0/app/src/templates/index.html +507 -0
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/pyproject.toml +9 -3
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/src/gh_space_shooter/github_client.py +1 -1
- gh_space_shooter-1.0.0/src/gh_space_shooter/py.typed +0 -0
- gh_space_shooter-1.0.0/uv.lock +1093 -0
- gh_space_shooter-0.0.5/.python-version +0 -1
- gh_space_shooter-0.0.5/uv.lock +0 -305
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/.github/dependabot.yml +0 -0
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/LICENSE +0 -0
- /gh_space_shooter-0.0.5/src/gh_space_shooter/py.typed → /gh_space_shooter-1.0.0/app/src/__init__.py +0 -0
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/example.gif +0 -0
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/src/gh_space_shooter/__init__.py +0 -0
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/src/gh_space_shooter/cli.py +0 -0
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/src/gh_space_shooter/console_printer.py +0 -0
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/src/gh_space_shooter/constants.py +0 -0
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/__init__.py +0 -0
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/animator.py +0 -0
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/drawables/__init__.py +0 -0
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/drawables/bullet.py +0 -0
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/drawables/drawable.py +0 -0
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/drawables/enemy.py +0 -0
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/drawables/explosion.py +0 -0
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/drawables/ship.py +0 -0
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/drawables/starfield.py +0 -0
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/game_state.py +0 -0
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/render_context.py +0 -0
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/renderer.py +0 -0
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/strategies/__init__.py +0 -0
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/strategies/base_strategy.py +0 -0
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/strategies/column_strategy.py +0 -0
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/strategies/random_strategy.py +0 -0
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/strategies/row_strategy.py +0 -0
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/tests/conftest.py +0 -0
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/tests/test_bullet_collision.py +0 -0
- {gh_space_shooter-0.0.5 → gh_space_shooter-1.0.0}/tests/test_strategies.py +0 -0
|
@@ -7,7 +7,7 @@ on:
|
|
|
7
7
|
types: [opened, synchronize, reopened]
|
|
8
8
|
|
|
9
9
|
env:
|
|
10
|
-
PYTHON_LATEST: 3.
|
|
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.
|
|
18
|
+
python-version: ["3.12"]
|
|
19
19
|
|
|
20
20
|
steps:
|
|
21
21
|
- name: Checkout code
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -22,6 +22,9 @@ uv run pytest tests/test_strategies.py -v
|
|
|
22
22
|
|
|
23
23
|
# Run a specific test
|
|
24
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
|
|
25
28
|
```
|
|
26
29
|
|
|
27
30
|
## Environment Setup
|
|
@@ -36,7 +39,7 @@ export GH_TOKEN=your_token_here
|
|
|
36
39
|
|
|
37
40
|
**Current main usage**: GitHub Action that automatically updates a game GIF in user repositories daily (see `.github/workflows/` for the action definition).
|
|
38
41
|
|
|
39
|
-
**
|
|
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.
|
|
40
43
|
|
|
41
44
|
## Architecture Overview
|
|
42
45
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gh-space-shooter
|
|
3
|
-
Version: 0.0
|
|
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.
|
|
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`:
|
|
@@ -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`:
|
|
@@ -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
|
+
```
|
|
Binary file
|
|
@@ -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}")
|