mirror of
https://github.com/rr-/pq-cli.git
synced 2026-09-29 14:54:00 -06:00
build: add Docker builds
Co-authored-by: Jakub Stastka <jakub@stastka.org>
This commit is contained in:
co-authored by
Jakub Stastka
parent
7426924170
commit
696fad78b3
+12
@@ -0,0 +1,12 @@
|
|||||||
|
FROM python:3.12-slim
|
||||||
|
|
||||||
|
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||||
|
PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN pip install --no-cache-dir .
|
||||||
|
|
||||||
|
CMD ["pqcli", "--basic"]
|
||||||
@@ -41,6 +41,43 @@ $ cd pq-cli
|
|||||||
$ pip install --user .
|
$ pip install --user .
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Docker / Docker Compose
|
||||||
|
|
||||||
|
The repository includes a `Dockerfile` and `docker-compose.yml` that run the
|
||||||
|
game with `--basic` and keep save data in a persistent named volume.
|
||||||
|
|
||||||
|
There is no registry pipeline required for this setup: build locally, then run
|
||||||
|
with Compose.
|
||||||
|
|
||||||
|
```console
|
||||||
|
# Build image locally from the Dockerfile
|
||||||
|
docker compose build
|
||||||
|
|
||||||
|
# First run (interactive): create your character in the shared save volume
|
||||||
|
docker compose run --rm pqcli-init
|
||||||
|
|
||||||
|
# Later runs on a server (detached, default save slot 1):
|
||||||
|
docker compose up -d pqcli
|
||||||
|
|
||||||
|
# List existing saves in the same volume:
|
||||||
|
docker compose run --rm pqcli-init pqcli --basic --list-saves
|
||||||
|
|
||||||
|
# Change detached slot if needed (example: slot 2):
|
||||||
|
PQCLI_SAVE_SLOT=2 docker compose up -d pqcli
|
||||||
|
|
||||||
|
# Follow logs / stop detached container:
|
||||||
|
docker compose logs -f pqcli
|
||||||
|
docker compose stop pqcli
|
||||||
|
```
|
||||||
|
|
||||||
|
On first run, if no save data exists in the mounted volume, `pqcli` will
|
||||||
|
automatically launch an interactive character-creation bootstrap in CLI mode
|
||||||
|
before starting the normal interface.
|
||||||
|
|
||||||
|
For server usage, run first-time setup without a forced slot (`pqcli-init`),
|
||||||
|
then run the long-lived detached service (`pqcli`) that loads slot `1` by
|
||||||
|
default.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
services:
|
||||||
|
pqcli-init:
|
||||||
|
build: .
|
||||||
|
image: ghcr.io/${GITHUB_REPOSITORY_OWNER:-local}/pq-cli:latest
|
||||||
|
stdin_open: true
|
||||||
|
tty: true
|
||||||
|
command: ["pqcli", "--basic"]
|
||||||
|
environment:
|
||||||
|
XDG_CONFIG_HOME: /data
|
||||||
|
volumes:
|
||||||
|
- pqcli-data:/data
|
||||||
|
|
||||||
|
pqcli:
|
||||||
|
build: .
|
||||||
|
stdin_open: true
|
||||||
|
tty: true
|
||||||
|
stop_signal: SIGINT
|
||||||
|
stop_grace_period: 30s
|
||||||
|
command: ["sh", "-c", "pqcli --basic --load-save ${PQCLI_SAVE_SLOT:-1}"]
|
||||||
|
environment:
|
||||||
|
XDG_CONFIG_HOME: /data
|
||||||
|
volumes:
|
||||||
|
- pqcli-data:/data
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
pqcli-data:
|
||||||
@@ -66,6 +66,21 @@ def list_players(roster: Roster, file: T.Optional[T.Any] = None) -> None:
|
|||||||
print(f"{i}. {player.name}", file=file)
|
print(f"{i}. {player.name}", file=file)
|
||||||
|
|
||||||
|
|
||||||
|
def bootstrap_first_run(roster: Roster, args: argparse.Namespace) -> None:
|
||||||
|
if roster.players or args.list_saves or args.load_save:
|
||||||
|
return
|
||||||
|
|
||||||
|
print("No saved characters found. Starting first-run character creation.")
|
||||||
|
ui = BasicUserInterface(roster, None, args)
|
||||||
|
while not roster.players:
|
||||||
|
player = ui.create_player(auto_play=False)
|
||||||
|
if player:
|
||||||
|
return
|
||||||
|
if not ui.confirm("No character created. Do you want to try again?"):
|
||||||
|
print("A character is required for first run.", file=sys.stderr)
|
||||||
|
raise SystemExit(1)
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
args = parse_args()
|
args = parse_args()
|
||||||
roster = Roster.load(SAVE_PATH)
|
roster = Roster.load(SAVE_PATH)
|
||||||
@@ -83,6 +98,8 @@ def main() -> None:
|
|||||||
list_players(roster, file=sys.stderr)
|
list_players(roster, file=sys.stderr)
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
|
bootstrap_first_run(roster, args)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
ui = args.ui(roster, player, args)
|
ui = args.ui(roster, player, args)
|
||||||
ui.run()
|
ui.run()
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ class BasicUserInterface(BaseUserInterface):
|
|||||||
self.quit()
|
self.quit()
|
||||||
break
|
break
|
||||||
|
|
||||||
def create_player(self) -> T.Optional[Player]:
|
def create_player(self, *, auto_play: bool = True) -> T.Optional[Player]:
|
||||||
name = input("Name your new character: ")
|
name = input("Name your new character: ")
|
||||||
if not name:
|
if not name:
|
||||||
print("Cancelled.")
|
print("Cancelled.")
|
||||||
@@ -123,7 +123,9 @@ class BasicUserInterface(BaseUserInterface):
|
|||||||
name=name, race=race, class_=class_, stats=stats
|
name=name, race=race, class_=class_, stats=stats
|
||||||
)
|
)
|
||||||
self.roster.players.append(player)
|
self.roster.players.append(player)
|
||||||
if self.confirm("Do you want to play as your new character?"):
|
if auto_play and self.confirm(
|
||||||
|
"Do you want to play as your new character?"
|
||||||
|
):
|
||||||
self.play(player)
|
self.play(player)
|
||||||
return player
|
return player
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ dependencies = [
|
|||||||
"xdg-base-dirs>=6.0.0,<7.0.0",
|
"xdg-base-dirs>=6.0.0,<7.0.0",
|
||||||
"urwid>=2.1.2,<3.0.0",
|
"urwid>=2.1.2,<3.0.0",
|
||||||
"urwid-readline>=0.13,<1.0.0",
|
"urwid-readline>=0.13,<1.0.0",
|
||||||
|
"pre-commit>=4.5.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ name = "pqcli"
|
|||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
{ name = "pre-commit" },
|
||||||
{ name = "urwid" },
|
{ name = "urwid" },
|
||||||
{ name = "urwid-readline" },
|
{ name = "urwid-readline" },
|
||||||
{ name = "xdg-base-dirs" },
|
{ name = "xdg-base-dirs" },
|
||||||
@@ -78,6 +79,7 @@ dev = [
|
|||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [
|
requires-dist = [
|
||||||
|
{ name = "pre-commit", specifier = ">=4.5.0" },
|
||||||
{ name = "urwid", specifier = ">=2.1.2,<3.0.0" },
|
{ name = "urwid", specifier = ">=2.1.2,<3.0.0" },
|
||||||
{ name = "urwid-readline", specifier = ">=0.13,<1.0.0" },
|
{ name = "urwid-readline", specifier = ">=0.13,<1.0.0" },
|
||||||
{ name = "windows-curses", marker = "extra == 'windows'", specifier = ">=2.3.0,<3.0.0" },
|
{ name = "windows-curses", marker = "extra == 'windows'", specifier = ">=2.3.0,<3.0.0" },
|
||||||
|
|||||||
Reference in New Issue
Block a user