Skip to content

Commit 9d13cbd

Browse files
authored
Merge pull request #23 from obilodeau/new-packaging-and-releasing
Packaging improvements and automated releases
2 parents 8154deb + 80d7456 commit 9d13cbd

42 files changed

Lines changed: 397 additions & 71 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/release.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
changelog:
2+
exclude:
3+
labels:
4+
- ignore-for-release
5+
categories:
6+
- title: Breaking changes
7+
labels:
8+
- breaking
9+
- title: Features
10+
labels:
11+
- feature
12+
- enhancement
13+
- title: Bug fixes
14+
labels:
15+
- fix
16+
- bug
17+
- title: Documentation
18+
labels:
19+
- docs
20+
- title: Dependencies
21+
labels:
22+
- dependencies
23+
- title: Other changes
24+
labels:
25+
- "*"

.github/workflows/ci.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,30 @@ jobs:
3838

3939
- name: Run CI checks
4040
run: make ci
41+
42+
build:
43+
name: Packaging dry-run
44+
runs-on: ubuntu-latest
45+
steps:
46+
- uses: actions/checkout@v4
47+
with:
48+
fetch-depth: 0 # hatch-vcs needs tags/history to compute the version
49+
50+
- name: Set up Python
51+
uses: actions/setup-python@v5
52+
with:
53+
python-version: "3.11"
54+
55+
- name: Set up Node.js
56+
uses: actions/setup-node@v4
57+
with:
58+
node-version: "20"
59+
cache: "npm"
60+
cache-dependency-path: frontend/package-lock.json
61+
62+
- name: Build wheel + sdist
63+
run: make build
64+
65+
- name: Verify the SPA shipped in the wheel
66+
run: |
67+
unzip -l dist/ceopardy-*.whl | grep -q "ceopardy/static/dist/index.html"

.github/workflows/release.yml

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
name: Release
2+
3+
# Triggered by pushing a SemVer tag (vMAJOR.MINOR.PATCH). The strict pattern
4+
# also rejects accidental tags like `release-1` or `0.6.0` (missing v).
5+
on:
6+
push:
7+
tags:
8+
- "v[0-9]+.[0-9]+.[0-9]+"
9+
10+
permissions:
11+
contents: write # create release + upload assets
12+
13+
jobs:
14+
build:
15+
name: Build wheel + sdist
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/checkout@v4
19+
with:
20+
fetch-depth: 0 # hatch-vcs needs full history + tags
21+
22+
- name: Verify tag commit is on main
23+
run: |
24+
git fetch --no-tags --depth=1 origin main
25+
if ! git merge-base --is-ancestor "$GITHUB_SHA" origin/main; then
26+
echo "::error::Tag commit $GITHUB_SHA is not on main; refusing to release."
27+
exit 1
28+
fi
29+
30+
- name: Set up Python
31+
uses: actions/setup-python@v5
32+
with:
33+
python-version: "3.11"
34+
35+
- name: Set up Node.js
36+
uses: actions/setup-node@v4
37+
with:
38+
node-version: "20"
39+
cache: "npm"
40+
cache-dependency-path: frontend/package-lock.json
41+
42+
- name: Build frontend bundle
43+
run: |
44+
npm --prefix frontend ci
45+
npm --prefix frontend run build
46+
47+
- name: Build sdist + wheel
48+
run: |
49+
pip install build
50+
python -m build
51+
52+
- name: Upload artifacts
53+
uses: actions/upload-artifact@v4
54+
with:
55+
name: dist
56+
path: dist/
57+
58+
release:
59+
name: Publish GitHub Release
60+
needs: build
61+
runs-on: ubuntu-latest
62+
steps:
63+
- uses: actions/download-artifact@v4
64+
with:
65+
name: dist
66+
path: dist/
67+
68+
- name: Create release
69+
uses: softprops/action-gh-release@v2
70+
with:
71+
files: dist/*
72+
generate_release_notes: true
73+
fail_on_unmatched_files: true

.gitignore

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ __pycache__/
33
*.pyc
44
/.venv/
55

6+
# packaging
7+
/build/
8+
/dist/
9+
*.egg-info/
10+
/ceopardy/_version.py
11+
612
# IDE files
713
/.idea/
814
/.vscode/
@@ -12,17 +18,17 @@ __pycache__/
1218
/frontend/node_modules/
1319

1420
# Vite build output
15-
/static/dist/
16-
17-
# Legacy static/vendor directory (no longer produced, kept for safety)
18-
/static/vendor/
21+
/ceopardy/static/dist/
1922

2023
# project files
2124
ceopardy*db
2225
ceopardy.log
26+
/game-media/
27+
# Operator game content — seeded by `ceopardy init` from package templates.
28+
/data/
2329

2430
# sound files (not provided)
25-
/static/sounds/*.mp3
26-
/static/sounds/*.wav
31+
/ceopardy/static/sounds/*.mp3
32+
/ceopardy/static/sounds/*.wav
2733
# these are creative commons sounds
28-
!/static/sounds/buzzer*.wav
34+
!/ceopardy/static/sounds/buzzer*.wav

Makefile

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# the user has activated it (or has direnv loaded). `make venv` creates it.
33
export PATH := $(CURDIR)/.venv/bin:$(PATH)
44

5-
.PHONY: ci lint format format-check test install-dev run venv
5+
.PHONY: ci lint format format-check test install-dev run venv init build
66

77
# Run all CI checks — called by GitHub Actions.
88
ci: lint format-check frontend-lint test
@@ -36,6 +36,19 @@ venv:
3636
install-dev:
3737
pip install -r requirements.txt -r requirements-dev.txt
3838

39+
# Seed CWD with example data/ and game-media/. Same as `ceopardy init`,
40+
# but works without an editable install (dev workflow uses python run.py).
41+
init:
42+
python -m ceopardy init
43+
44+
# Build sdist + wheel (the same path the release workflow takes). Requires
45+
# the frontend bundle to be present: rebuilds it first.
46+
build:
47+
npm --prefix frontend ci
48+
npm --prefix frontend run build
49+
pip install build
50+
python -m build
51+
3952
# ── Run dev servers (Flask + Vite) in one terminal ──────────────────────────
4053

4154
# Reinstalls when package.json is newer than the install marker.
Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,60 @@
1-
= Ceopardy
1+
# Ceopardy
22

33
The Hacker Jeopardy Game Board we use at NorthSec.
44

5-
== Screenshots
5+
## Screenshots
66

77
This is what the crowd sees:
88

9-
image::docs/images/viewer-board.png[The Viewer Interface Displaying the Game Board]
9+
![The Viewer Interface Displaying the Game Board](docs/images/viewer-board.png)
1010

1111
When a clue is displayed:
1212

13-
image::docs/images/viewer-clue.png[The Viewer Interface Displaying a Clue]
13+
![The Viewer Interface Displaying a Clue](docs/images/viewer-clue.png)
1414

1515
This is the host interface, how you control the game:
1616

17-
image::docs/images/host.png[The Host Interface]
17+
![The Host Interface](docs/images/host.png)
1818

1919
Note that there are two drawers that can be opened by clicking on the brown
2020
arrows at the top and at the bottom of the screen. The top drawer contains
2121
the functions to change team names. The bottom drawer provides functions to
2222
display a custom message on the board or to pause a game.
2323

2424

25-
== Architecture
25+
## Architecture
2626

2727
Starting with v0.5, Ceopardy is split in two parts:
2828

29-
* A Python/Flask back-end that exposes a small REST API (`/api/v1/...`) and
29+
- A Python/Flask back-end that exposes a small REST API (`/api/v1/...`) and
3030
broadcasts state changes over a single Socket.IO namespace (`/game`).
31-
* A Vite + Vue 3 front-end (in `frontend/`) that powers the crowd-facing
31+
- A Vite + Vue 3 front-end (in `frontend/`) that powers the crowd-facing
3232
viewer, the host UI, and the start screen.
3333

3434

35-
== First time deployment
35+
## First time deployment
3636

3737
You need Python, pip, virtualenv and Node.js (LTS). The tl;dr:
3838

3939
make venv
4040
source .venv/bin/activate # bash/zsh
4141
source .venv/bin/activate.fish # fish
42+
make init # seed data/ and game-media/ from templates
4243
npm install --prefix frontend
4344
npm run build --prefix frontend
4445
python run.py
4546

4647
`make venv` creates `.venv/` and installs both runtime and dev requirements.
48+
`make init` is a one-time step that copies starter `data/1st.round` and
49+
`data/Questions.cp` into the repo and creates `game-media/`. Edit those files
50+
to set up your game.
4751

48-
=== Optional: direnv
52+
### Optional: direnv
4953

50-
If you use https://direnv.net/[direnv], the repo ships an `.envrc` that puts
54+
If you use [direnv](https://direnv.net/), the repo ships an `.envrc` that puts
5155
`.venv/bin` on your `PATH` automatically when you `cd` into the directory —
5256
works in bash, zsh, and fish. Install direnv (see
53-
https://direnv.net/docs/installation.html[upstream docs] for shell hook setup),
57+
[upstream docs](https://direnv.net/docs/installation.html) for shell hook setup),
5458
then from the repo root:
5559

5660
make venv # create the venv first; direnv won't do this for you
@@ -59,8 +63,8 @@ then from the repo root:
5963
After that, entering the directory activates the venv and leaving deactivates
6064
it — no manual `source` needed.
6165

62-
Then open http://127.0.0.1:5000/host[the host view] to set up the game.
63-
http://127.0.0.1:5000/[The players' view] (also known as the viewer) can be
66+
Then open [the host view](http://127.0.0.1:5000/host) to set up the game.
67+
[The players' view](http://127.0.0.1:5000/) (also known as the viewer) can be
6468
opened at any time.
6569

6670
`python run.py` runs the built-in dev server (debug + reloader). For
@@ -74,7 +78,7 @@ Put nginx (or similar) in front for TLS and to expose it on the network — the
7478
app itself binds to localhost because the host interface has no auth.
7579

7680

77-
== Development
81+
## Development
7882

7983
Run Flask and Vite side by side. Vite hot-reloads the UI and proxies
8084
`/api` and `/socket.io` to Flask.
@@ -88,26 +92,32 @@ Run Flask and Vite side by side. Vite hot-reloads the UI and proxies
8892
Then open http://localhost:5173/.
8993

9094

91-
== Setup
95+
## Install as a CLI (pipx)
9296

93-
=== Display
97+
Once published, Ceopardy can be installed as a stand-alone command:
9498

95-
You need at least 2 outputs: one for the game host for control and one for the
96-
public.
99+
pipx install ceopardy
100+
mkdir my-game && cd my-game
101+
ceopardy init # scaffolds data/ and game-media/ in CWD
102+
# edit data/Questions.cp and data/1st.round
103+
ceopardy # or `ceopardy serve` — starts the server
97104

98-
At NorthSec 2017, we used 3 outputs because we didn't have the proper gear to
99-
duplicate the public output for a stage monitor. There is a script in
100-
`helpers/` that will set 3 outputs using `xrandr`. It didn't work with a GUI
101-
tool when we tried at that time.
105+
`ceopardy init` never overwrites existing files; it's safe to re-run. The
106+
server, the SQLite database, and the question files all resolve relative to
107+
the directory you run `ceopardy` from, so keep one directory per game.
102108

103-
== Prepare a game
109+
110+
## Prepare a game
104111

105112
Game data goes in `data/`. There you should add round files (create a `.round`
106113
file) and questions in `Questions.cp`. The format is pretty self explanatory.
107-
Check `data/` for an example.
108-
109-
NOTE: In order to avoid dataloss due to a crash, Ceopardy is backed by a
110-
database where transactions are pushed when the hosts submit the points. This
111-
has the flipside requiring games to be finalized before a new one can be
112-
started. Make sure that you always push the "Game over" button before
113-
reloading to start a new game.
114+
Run `ceopardy init` (or `make init` from the repo) to get a working starter
115+
set; `data/1st.round` and `data/Questions.cp` are the minimal example.
116+
User-supplied media referenced by questions (e.g. `[img:photo.png]`) goes in
117+
`game-media/` next to `data/`.
118+
119+
> **Note:** In order to avoid dataloss due to a crash, Ceopardy is backed by a
120+
> database where transactions are pushed when the hosts submit the points. This
121+
> has the flipside requiring games to be finalized before a new one can be
122+
> started. Make sure that you always push the "Game over" button before
123+
> reloading to start a new game.

0 commit comments

Comments
 (0)