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
43 changes: 43 additions & 0 deletions .ai/INDEX.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# xa-transactions — agent context

## What this is

Python **3.10+** library: coordinate **MySQL XA (two-phase commit)** across **parallel workers** (e.g. Celery) with **atomic commit or rollback** across branches. Optional **Django** and **Celery** integrations.

## Where code lives

| Area | Path | Role |
|------|------|------|
| Coordinator, XA adapter, MySQL store | `xa_transactions/core/` | Orchestration, XA SQL, durable branch/global state |
| Integrations | `xa_transactions/integrations/` | Celery (`celery` extra), Django (optional) |
| Recovery, connections | `xa_transactions/infrastructure/` | Recovery strategy, connection factories |
| Types & protocols | `xa_transactions/types/` | `StoreProtocol`, `XAAdapterProtocol`, errors, XID types |
| Observability | `xa_transactions/observability/` | Hooks and metrics helpers |
| Public exports | `xa_transactions/__init__.py` | Stable surface for imports |

## Human docs (read before large design changes)

- [README.md](../README.md) — overview and quick start
- [CHANGELOG.md](../CHANGELOG.md) — release history (update `[Unreleased]` when changing user-visible behavior)
- [ARCHITECTURE.md](../ARCHITECTURE.md) — design detail
- [docs/CELERY.md](../docs/CELERY.md) — Celery usage
- [docs/DJANGO.md](../docs/DJANGO.md) — Django usage

## Local environment

Use a **venv** and **pip** — see **Development** in [README.md](../README.md) (`python3 -m venv .venv`, activate, `pip install -e ".[dev]"`). Optional: [scripts/check_dev_dependencies.sh](../scripts/check_dev_dependencies.sh) (**macOS only**, **Homebrew**; assumes **git**; interactive **y/n/a**; **`--dry-run`** / **`-y`**) then [scripts/setup_local_env.sh](../scripts/setup_local_env.sh) (venv + editable install).

## Verify changes

- **Pre-commit** (required): `pip install -e ".[dev]"` includes the `pre-commit` package; run **`pre-commit install`** once per clone (or use [scripts/setup_local_env.sh](../scripts/setup_local_env.sh) with default `PIP_EXTRAS=dev`). Hooks: Ruff (`xa_transactions/`, `tests/` only, same as CI) + unit-only `pytest` ([.pre-commit-config.yaml](../.pre-commit-config.yaml), [scripts/pre_commit_pytest_unit.sh](../scripts/pre_commit_pytest_unit.sh)). Manual: `pre-commit run --all-files`.
- **Lint / format**: `ruff check xa_transactions tests` and `ruff format --check xa_transactions tests` (after `pip install -e ".[dev]"`; see [README.md](../README.md)).
- **Unit tests + coverage**: `pytest --cov=xa_transactions --cov-report=term-missing` (default run skips `@pytest.mark.celery` / `django` tests; see **Testing** in README).
- **Optional integration tests**: `pip install -e ".[dev,celery,django]"` then `pytest -m "celery or django" -v`.
- **CI**: [.github/workflows/ci.yml](../.github/workflows/ci.yml) — matrix 3.10–3.12 on Ubuntu. After merge to `main`, [.github/workflows/tag-on-main.yml](../.github/workflows/tag-on-main.yml) creates `v<version>` from `pyproject.toml` if missing.

## Conventions for edits

- **Commits**: [Commitizen](https://commitizen-tools.github.io/commitizen/) — `git cz` (included in **`[dev]`**); config in [`pyproject.toml`](../pyproject.toml) under `[tool.commitizen]`. **`cz bump`** bumps version and updates [`CHANGELOG.md`](../CHANGELOG.md); **`cz changelog --incremental`** refreshes changelog without a bump.
- Prefer **type hints** (`T | None`, not `Optional[T]`, without relying on `from __future__ import annotations` — **Python 3.10+** is required).
- Treat **`StoreProtocol`** and **`xa_transactions/__init__.py`** exports as API: breaking changes need intent and changelog consideration.
- Optional deps: Celery/Django code paths should remain import-safe when extras are not installed (see patterns in `__init__.py`).
24 changes: 24 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
## Summary

<!-- What does this PR change and why? Link to issue/ticket if any. -->

## Type of change

<!-- Check one or more -->

- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation only
- [ ] Chore (tooling, CI, refactor without behavior change)

## Checklist

- [ ] **Commits** follow our convention — use **[Commitizen](https://commitizen-tools.github.io/commitizen/)** (`git cz`) or equivalent [Conventional Commits](https://www.conventionalcommits.org/) (`feat:`, `fix:`, `chore:`, …).
- [ ] **`CHANGELOG.md`** updated under `[Unreleased]` (or note **N/A** for internal-only churn).
- [ ] **Tests**: `pytest` / `pre-commit run --all-files` as appropriate (see README **Testing**).
- [ ] **Docs** touched if behavior or public API changed (README, `ARCHITECTURE.md`, `docs/`, etc.).

## Notes for reviewers

<!-- Optional: risk areas, follow-ups, screenshots. -->
51 changes: 51 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: CI

on:
push:
branches: [main]
pull_request:

jobs:
unit:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install system dependencies (mysqlclient)
run: sudo apt-get update && sudo apt-get install -y default-libmysqlclient-dev pkg-config
- name: Install Python dependencies
run: |
python -m pip install -U pip
python -m pip install -e ".[dev]"
- name: Ruff
run: |
ruff check xa_transactions tests
ruff format --check xa_transactions tests
- name: Unit tests with coverage
run: pytest --cov=xa_transactions --cov-report=term-missing

integration:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install system dependencies (mysqlclient)
run: sudo apt-get update && sudo apt-get install -y default-libmysqlclient-dev pkg-config
- name: Install Python dependencies
run: |
python -m pip install -U pip
python -m pip install -e ".[dev,celery,django]"
- name: Optional integration tests
run: pytest -m "celery or django" -v
44 changes: 44 additions & 0 deletions .github/workflows/tag-on-main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# On push to main, create git tag v<version> from [project].version in pyproject.toml
# if that tag does not already exist on the remote.

name: Tag version on main

on:
push:
branches: [main]

permissions:
contents: write

concurrency:
group: tag-on-main
cancel-in-progress: true

jobs:
tag:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Read version from pyproject.toml
id: meta
run: |
VERSION="$(python3 -c "import tomllib; f=open('pyproject.toml','rb'); print(tomllib.load(f)['project']['version'])")"
echo "version=${VERSION}" >> "${GITHUB_OUTPUT}"
echo "tag=v${VERSION}" >> "${GITHUB_OUTPUT}"

- name: Create and push tag if missing
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG="${{ steps.meta.outputs.tag }}"
if git ls-remote --tags origin "refs/tags/${TAG}" | grep -q .; then
echo "Tag ${TAG} already exists — skipping."
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag -a "${TAG}" -m "Release ${TAG}"
git push "https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" "refs/tags/${TAG}"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ wheels/
*.egg

# Virtual environments
.venv/
venv/
env/
ENV/
Expand Down
24 changes: 24 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Requires: pip install -e ".[dev]" (includes pre-commit), then pre-commit install.
# Run manually: pre-commit run --all-files

repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.9
hooks:
- id: ruff
args: [--fix, xa_transactions, tests]
pass_filenames: false
always_run: true
- id: ruff-format
args: [xa_transactions, tests]
pass_filenames: false
always_run: true

- repo: local
hooks:
- id: pytest-unit
name: pytest (unit only; excludes celery/django markers)
entry: bash scripts/pre_commit_pytest_unit.sh
language: system
pass_filenames: false
always_run: true
66 changes: 66 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Changelog

All notable changes to this project are documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

Commit messages follow the [Conventional Commits](https://www.conventionalcommits.org/) style; use [Commitizen](https://commitizen-tools.github.io/commitizen/) (`git cz`) when committing.

## [Unreleased]

### Added

- **GitHub Actions:** [`.github/workflows/tag-on-main.yml`](.github/workflows/tag-on-main.yml) — on push to `main`, create `v<version>` from `pyproject.toml` if missing on the remote.
- **Unit tests:** expanded [`tests/test_types.py`](tests/test_types.py); [`tests/test_coordinator_unit.py`](tests/test_coordinator_unit.py) exercises `Coordinator` with mocked store/adapter (finalize, reconcile_branch, validation paths).

### Changed

### Fixed

### Removed

---

## [0.2.0] - 2026-04-02

### Added

- **XA `format_id`**: `XID` includes `format_id`; propagated through `MySQLXAAdapter`, `Coordinator`, `create_coordinator`, recovery (`DefaultRecoveryStrategy`), and Celery XA context (`XATask`, `xa_task`).
- **`py.typed`** marker for PEP 561 type-checker support.
- **`[tool.mypy]`** in `pyproject.toml` (aligned with Python 3.10).
- **Pre-commit**: `.pre-commit-config.yaml` — Ruff (lint + format) on `xa_transactions/` and `tests/`; unit-only `pytest` (excludes `celery` / `django` markers) via `scripts/pre_commit_pytest_unit.sh` (prefers `.venv/bin/python`).
- **`pre-commit`** in the `dev` optional dependency set; `scripts/setup_local_env.sh` runs `pre-commit install` when extras include `dev`.
- **CI** (GitHub Actions): unit job (Ruff + pytest with coverage) and integration job (Celery/Django marked tests) on Python 3.10–3.12.
- **Scripts**: `scripts/check_dev_dependencies.sh` (macOS/Homebrew helper), `scripts/setup_local_env.sh`.
- **`.ai/INDEX.md`**: agent-oriented repo overview and verification commands.
- **Integration smoke tests**: `tests/integration/` for `@pytest.mark.celery` and `@pytest.mark.django` (import/symbol checks).

### Changed

- **`requires-python`**: `>=3.10` (was `>=3.9` on the feature branch before merge).
- **Django integration**: `xa_aware_atomic` implemented with a `ContextDecorator`-style helper so Django’s `atomic` usage patterns (context manager, decorator, callable `using`) behave correctly.
- **README**: development setup, testing (unit vs integration), required pre-commit workflow.

### Fixed

- Django `transaction.atomic` wrapper behavior for all supported usage patterns.
- Typing and error-handling cleanups on the format-id / mypy branch.
- Ruff-driven import and style fixes in Django integration after merge.

### Removed

- (none noted)

---

## [0.1.0] - 2026-02-23

### Added

- Initial **xa-transactions** library: MySQL XA coordination (`Coordinator`, `MySQLXAAdapter`, `MySQLStore`), protocols, recovery, observability hooks/metrics, optional Celery and Django integrations.
- **Architecture** and diagram updates in-repo.

[Unreleased]: https://github.com/cube-planning/xa_transactions/compare/v0.2.0...HEAD
[0.2.0]: https://github.com/cube-planning/xa_transactions/compare/v0.1.0...v0.2.0
[0.1.0]: https://github.com/cube-planning/xa_transactions/releases/tag/v0.1.0
60 changes: 60 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ graph TD

## Installation

Requires **Python 3.10+**.

```bash
pip install xa-transactions
```
Expand All @@ -54,6 +56,64 @@ For Celery integration:
pip install xa-transactions[celery]
```

## Development

Release history: [CHANGELOG.md](CHANGELOG.md). Commits use [Conventional Commits](https://www.conventionalcommits.org/) via [Commitizen](https://commitizen-tools.github.io/commitizen/) (`pip install -e ".[dev]"` includes `cz`; use **`git cz`** to commit).

**Changelog automation** ([`pyproject.toml`](pyproject.toml) `[tool.commitizen]`): **`cz bump`** updates **`CHANGELOG.md`** from the commit range since the last tag (`changelog_incremental`, `update_changelog_on_bump`). To regenerate without bumping: **`cz changelog --incremental`** (preview with **`cz changelog --dry-run --incremental`**). Generated sections use Conventional Commit types (**feat** / **fix** / …), not always Keep a Changelog’s **Added** / **Changed** — edit for wording or categories after generation if you care.

**Tags on `main`:** after each merge to `main`, [`.github/workflows/tag-on-main.yml`](.github/workflows/tag-on-main.yml) creates the git tag `v<version>` from `[project].version` in [`pyproject.toml`](pyproject.toml) if that tag does not already exist on the remote (bump the version in `pyproject.toml` when you release).

Use a **virtual environment** and **pip** (do not install into the system interpreter).

From the repository root:

```bash
python3 -m venv .venv
source .venv/bin/activate # Linux / macOS
# .venv\Scripts\activate # Windows (cmd/PowerShell)
python -m pip install -U pip
python -m pip install -e ".[dev]"
pre-commit install
```

**Required for development:** the **`dev`** extra installs the editable package plus **Ruff**, **pytest**, **pytest-cov**, and the **`pre-commit`** CLI. You must run **`pre-commit install`** once per clone (after the venv has those packages) so Git runs the hooks; [`scripts/setup_local_env.sh`](scripts/setup_local_env.sh) does this automatically when extras include `dev`.

Hooks (see [`.pre-commit-config.yaml`](.pre-commit-config.yaml)): **Ruff** on `xa_transactions/` and `tests/` (same scope as CI) and **unit tests only** via [`scripts/pre_commit_pytest_unit.sh`](scripts/pre_commit_pytest_unit.sh) (prefers `.venv/bin/python`; same `-m "not celery and not django"` as default `pytest`). Integration tests with `@pytest.mark.celery` / `django` are not run in the hook. Run the same checks manually with `pre-commit run --all-files`.

Optional extras: `pip install -e ".[dev,celery]"` if you need Celery locally (still use `dev` for hooks and tooling).

Building **`mysqlclient`** may require MySQL/MariaDB client libraries and build tools on your OS; if install fails, use **`PyMySQL`** (already a dependency) or install the client headers first.

Optional helpers (same end state as the commands above):

```bash
./scripts/check_dev_dependencies.sh # macOS only: summarize missing deps, then [y/n/a] per install (a = yes to rest); full report after
./scripts/check_dev_dependencies.sh --dry-run # only missing deps, one line each (missing:… / optional:…)
./scripts/check_dev_dependencies.sh -y # non-interactive: brew + pyenv (does not change default python3 / pyenv global)
./scripts/setup_local_env.sh # create .venv, pip install -e ".[dev]", pre-commit install
```

### Testing

**PR / default local run:** **Ruff** + **unit tests** (excludes optional integration tests):

```bash
ruff check xa_transactions tests && ruff format --check xa_transactions tests
pytest --cov=xa_transactions --cov-report=term-missing
```

Do not reduce coverage without a good reason (review in PRs).

**Optional integrations** (Celery / Django) are behind pytest markers. Install extras, then run only those tests:

```bash
pip install -e ".[dev,celery,django]"
pytest -m "celery or django" -v
```

CI runs **Linux × Python 3.10–3.12**: unit job (ruff + default `pytest`), plus a separate job for marked integration tests with extras. Releases should go out only when CI is green on the tagged commit.

## Quick Start

```python
Expand Down
Loading
Loading