refactor, split to two versions - cli and service

This commit is contained in:
Jev
2026-05-25 23:28:24 +02:00
parent 79d86961e3
commit 7188b20797
14 changed files with 335 additions and 69 deletions
+50 -30
View File
@@ -1,10 +1,13 @@
# type: ignore
import re
from pathlib import Path
from invoke import task
PYPROJECT = Path(__file__).parent / "pyproject.toml"
{%- if cookiecutter.project_type == "service" %}
IMAGE = "{{ cookiecutter.project_slug }}"
SERVICE = "{{ cookiecutter.project_slug }}"
VPS = "vps" # ssh alias; edit to match your ~/.ssh/config
REMOTE_DIR = "~/stack" # dir on the VPS holding docker-compose.yml
{%- endif %}
@task
@@ -35,34 +38,27 @@ def test(c):
@task
def ci(c):
"""Run lint and tests in a clean Docker container."""
image = "{{ cookiecutter.project_slug }}-ci"
c.run(f"docker build --network=host -t {image} .")
cmd = (
"uv run ruff check src tests && "
"uv run ruff format --check src tests && "
"uv run mypy src && "
"uv run pytest --cov=src --cov-report=term-missing"
)
c.run(f"docker run --rm {image} sh -c '{cmd}'")
"""Run lint and tests."""
lint(c)
test(c)
print("All checks passed!")
@task
def _latest_tag(c) -> str:
result = c.run("git describe --tags --abbrev=0", hide=True, warn=True)
return result.stdout.strip() if result.ok else "v0.0.0"
@task(help={"part": "patch, minor, or major"})
def bump(c, part):
"""Bump version (patch/minor/major), commit, and tag."""
"""Tag a new semver release (git tag is the source of truth)."""
if part not in ("patch", "minor", "major"):
raise SystemExit("Usage: inv bump <patch|minor|major>")
result = c.run("git status --porcelain", hide=True)
if result.stdout.strip():
if c.run("git status --porcelain", hide=True).stdout.strip():
raise SystemExit("Working tree is dirty. Commit or stash changes first.")
text = PYPROJECT.read_text()
m = re.search(r'version = "(\d+)\.(\d+)\.(\d+)"', text)
if not m:
raise SystemExit("Could not find version in pyproject.toml")
major, minor, patch = int(m[1]), int(m[2]), int(m[3])
major, minor, patch = (int(x) for x in _latest_tag(c).lstrip("v").split("."))
if part == "major":
major, minor, patch = major + 1, 0, 0
elif part == "minor":
@@ -70,13 +66,37 @@ def bump(c, part):
else:
patch += 1
new_version = f"{major}.{minor}.{patch}"
new_text = re.sub(r'(version = ")\d+\.\d+\.\d+(")', rf"\g<1>{new_version}\2", text)
PYPROJECT.write_text(new_text)
tag = f"v{major}.{minor}.{patch}"
c.run(f"git tag {tag}")
print(f"Tagged {tag}. Push it with: git push origin {tag}")
{%- if cookiecutter.project_type == "service" %}
c.run(f'git add pyproject.toml && git commit -m "bump version to {new_version}"')
c.run(f"git tag v{new_version}")
print(f"Bumped to {new_version}")
def _version(c) -> str:
return _latest_tag(c).lstrip("v")
@task
def build_image(c):
"""Build the runtime image, tagged with the current version."""
c.run(f"docker build --network=host -t {IMAGE}:{_version(c)} .")
@task
def deploy(c):
"""Build image, copy to VPS over SSH, restart the compose service."""
version = _version(c)
build_image(c)
print(f"Transferring {IMAGE}:{version} to {VPS}...")
c.run(f"docker save {IMAGE}:{version} | ssh {VPS} docker load", pty=True)
print("Updating docker-compose and restarting service...")
c.run(
f'ssh {VPS} "cd {REMOTE_DIR} && '
f"sed -i 's|image: {IMAGE}:.*|image: {IMAGE}:{version}|' docker-compose.yml && "
f'docker compose up -d {SERVICE}"'
)
print(f"Deployed {IMAGE}:{version}")
{%- endif %}
@task