Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Lint

on:
pull_request:
types: [opened, synchronize, reopened]

env:
CI: true

permissions:
contents: read

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v6
- name: Install dependencies
run: uv sync --all-packages
- name: Ruff check
run: uv run ruff check .
- name: Ruff format check
run: uv run ruff format --check .
- name: Mypy
run: uv run mypy .
59 changes: 59 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Release

on:
push:
branches: [main]

concurrency: ${{ github.workflow }}-${{ github.ref }}

permissions:
contents: write
pull-requests: write

jobs:
release:
name: Release
runs-on: ubuntu-latest
environment:
name: release
outputs:
published: ${{ steps.sampo.outputs.published }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: astral-sh/setup-uv@v6
- run: uv sync --all-packages
- name: Create Release Pull Request or Tag
id: sampo
uses: bruits/sampo/crates/sampo-github-action@main
with:
command: auto
create-github-release: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

publish:
name: Publish to PyPI
needs: release
if: needs.release.outputs.published == 'true'
runs-on: ubuntu-latest
environment:
name: release
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v6
- name: Build all packages
run: |
for pkg in packages/*/; do
pkg_name=$(basename "$pkg")
if grep -q '\[build-system\]' "$pkg/pyproject.toml" 2>/dev/null; then
echo "Building $pkg_name..."
uv build --package "$pkg_name" --out-dir dist/
fi
done
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
22 changes: 22 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Test

on:
pull_request:
types: [opened, synchronize, reopened]

env:
CI: true

permissions:
contents: read

jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v6
- name: Install dependencies
run: uv sync --all-packages
- name: Run tests
run: uv run pytest
Empty file added .sampo/changesets/.gitkeep
Empty file.
13 changes: 13 additions & 0 deletions .sampo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[git]
default_branch = "main"

[github]
repository = "generaltranslation/gt-python"

[changelog]
show_commit_hash = true
show_acknowledgments = true

[packages]
ignore_unpublished = true
ignore = ["examples/*"]
67 changes: 41 additions & 26 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ A good bug report shouldn't leave others needing to chase you up for more inform

#### How Do I Submit a Good Bug Report?

> You must never report security related issues, vulnerabilities or bugs including sensitive information to the issue tracker, or elsewhere in public. Instead sensitive bugs must be sent by email to <security@generaltranslation.com>.
> You must never report security related issues, vulnerabilities or bugs including sensitive information to the issue tracker, or elsewhere in public. Instead sensitive bugs must be sent by email to <support@generaltranslation.com>.

We use GitHub issues to track bugs and errors. If you run into an issue with the project:

Expand All @@ -86,7 +86,7 @@ Enhancement suggestions are tracked as [GitHub issues](https://github.com/genera
### Your First Code Contribution

1. Fork the repository
2. Create a feature branch (`git checkout -b feature/my-feature`)
2. Create a feature/fix/refactor/etc. branch (`git checkout -b feature/my-feature`)
3. Follow the [Development Setup](#development-setup) instructions below
4. Make your changes
5. Run tests and linting to make sure everything passes
Expand All @@ -97,25 +97,25 @@ Enhancement suggestions are tracked as [GitHub issues](https://github.com/genera
### Prerequisites

- **Python 3.10+**
- **[uv](https://docs.astral.sh/uv/)** — package manager and workspace tool

Install uv:

```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```

### Installation

Clone the repository and install all dependencies:
Clone the repository and run the setup script:

```bash
git clone https://github.com/generaltranslation/gt-python.git
cd gt-python
uv sync --all-packages
./scripts/setup.sh
```

This creates a virtual environment at `.venv/` and installs all workspace packages as editable installs. Changes to source code are immediately reflected — no rebuild needed.
The setup script installs everything you need:

- [uv](https://docs.astral.sh/uv/) — package manager and workspace tool
- All workspace packages (as editable installs in `.venv/`)
- [Rust/Cargo](https://rustup.rs/) — needed to install Sampo
- [Sampo](https://github.com/bruits/sampo) — release automation tool

Changes to source code are immediately reflected — no rebuild needed.

### Recommended Editor Extensions

Expand All @@ -124,34 +124,44 @@ If you're using VS Code, install the following extensions:
| Extension | ID | Purpose |
| --------- | -- | ------- |
| Python | `ms-python.python` | IntelliSense, debugging, virtualenv support (includes Pylance) |
| Mypy Type Checker | `ms-python.mypy-type-checker` | Inline mypy type errors (matches project config) |
| Ruff | `charliermarsh.ruff` | Linting and formatting (matches project config) |
| Even Better TOML | `tamasfe.even-better-toml` | Syntax highlighting for `pyproject.toml` |

### Common Commands

```bash
# Install all workspace packages
uv sync --all-packages
make setup # Install uv, workspace packages, and sampo
make lint # Run ruff linter
make format # Auto-format with ruff
make format-check # Check formatting without changes
make typecheck # Run mypy type checking
make test # Run all tests
make check # Run all checks (lint + format + typecheck + test)
make build # Build all packages to dist/
make clean # Remove build artifacts and caches
```

# Run all tests
uv run pytest
To run tests for a specific package:

# Run tests for a specific package
```bash
uv run pytest packages/generaltranslation/
```

# Lint
uv run ruff check .

# Format
uv run ruff format .
### Releasing

# Type check
uv run mypy .
This project uses [Sampo](https://github.com/bruits/sampo) for automated releases. When you make a change that should be released, add a changeset:

# Build a specific package
uv build --package generaltranslation
```bash
sampo add
```

This prompts you to select affected packages and the bump type (patch/minor/major), then creates a changeset file in `.sampo/changesets/`. Commit this file with your PR.

When your PR merges to `main`, a GitHub Action automatically creates a Release PR that bumps versions and updates changelogs. Merging that Release PR publishes the packages to PyPI.

See `guides/releasing.md` for the full workflow.

## Styleguides

### Code Style
Expand All @@ -165,8 +175,13 @@ This project uses [Ruff](https://docs.astral.sh/ruff/) for linting and formattin

### Commit Messages

Please use [Semantic Commit Messages](https://gist.github.com/joshbuchea/6f47e86d2510bce28f8e7f42ae84c716).
For the scope, you can also mention the package names.
Use clear, descriptive commit messages that explain the "why" behind the change.

These commit conventions are generally most important on commits to main.
All branch commits get squash-merged.

## Join The Project Team

Interested in joining the team? Reach out on [Discord](https://discord.gg/W99K6fchSu) or email us at [hello@generaltranslation.com](mailto:hello@generaltranslation.com).
Expand Down
40 changes: 40 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
.PHONY: setup lint lint-fix format typecheck test check build clean

setup:
./scripts/setup.sh

lint:
uv run ruff check .

lint-fix:
uv run ruff check --fix .

format:
uv run ruff format .

format-check:
uv run ruff format --check .

typecheck:
uv run mypy .

test:
uv run pytest

check: lint format-check typecheck test

build:
@for pkg in packages/*/; do \
pkg_name=$$(basename "$$pkg"); \
if grep -q '\[build-system\]' "$$pkg/pyproject.toml" 2>/dev/null; then \
echo "Building $$pkg_name..."; \
uv build --package "$$pkg_name" --out-dir dist/; \
fi; \
done

clean:
rm -rf dist/
find . -type d -name __pycache__ -exec rm -rf {} +
find . -type d -name '*.egg-info' -exec rm -rf {} +
find . -type d -name .mypy_cache -exec rm -rf {} +
find . -type d -name .ruff_cache -exec rm -rf {} +
4 changes: 1 addition & 3 deletions examples/fastapi-eager/scripts/test_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@
BASE_URL = "http://localhost:8000"


def test_endpoint(
path: str, locale: str, expected_substr: str, name: str
) -> None:
def test_endpoint(path: str, locale: str, expected_substr: str, name: str) -> None:
headers = {"Accept-Language": locale}
resp = httpx.get(f"{BASE_URL}{path}", headers=headers)
body = resp.json()
Expand Down
1 change: 0 additions & 1 deletion examples/fastapi-eager/tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

from fastapi import FastAPI
from fastapi.testclient import TestClient

from gt_fastapi import initialize_gt, t

TRANSLATIONS = {
Expand Down
4 changes: 1 addition & 3 deletions examples/fastapi-lazy/scripts/test_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@
BASE_URL = "http://localhost:8001"


def test_endpoint(
path: str, locale: str, expected_substr: str, name: str
) -> None:
def test_endpoint(path: str, locale: str, expected_substr: str, name: str) -> None:
headers = {"Accept-Language": locale}
resp = httpx.get(f"{BASE_URL}{path}", headers=headers)
body = resp.json()
Expand Down
1 change: 0 additions & 1 deletion examples/fastapi-lazy/tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

from fastapi import Depends, FastAPI, Request
from fastapi.testclient import TestClient

from gt_fastapi import initialize_gt, t

TRANSLATIONS = {
Expand Down
4 changes: 1 addition & 3 deletions examples/flask-eager/scripts/test_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@
BASE_URL = "http://localhost:5050"


def test_endpoint(
path: str, locale: str, expected_substr: str, name: str
) -> None:
def test_endpoint(path: str, locale: str, expected_substr: str, name: str) -> None:
headers = {"Accept-Language": locale}
resp = httpx.get(f"{BASE_URL}{path}", headers=headers)
body = resp.json()
Expand Down
1 change: 0 additions & 1 deletion examples/flask-eager/tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"""

from flask import Flask

from gt_flask import initialize_gt, t

TRANSLATIONS = {
Expand Down
4 changes: 1 addition & 3 deletions examples/flask-lazy/scripts/test_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@
BASE_URL = "http://localhost:5051"


def test_endpoint(
path: str, locale: str, expected_substr: str, name: str
) -> None:
def test_endpoint(path: str, locale: str, expected_substr: str, name: str) -> None:
headers = {"Accept-Language": locale}
resp = httpx.get(f"{BASE_URL}{path}", headers=headers)
body = resp.json()
Expand Down
1 change: 0 additions & 1 deletion examples/flask-lazy/tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import asyncio

from flask import Flask

from gt_flask import initialize_gt, t

TRANSLATIONS = {
Expand Down
Loading