Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
c9716a8
ci: always auto-tag master merges and align release channels
EshwarCVS Apr 14, 2026
ab88e51
fix: resolve ruff I001 and mypy no-any-return errors blocking Python …
EshwarCVS Apr 14, 2026
02bb574
ci: two-branch model (stage + master), fix ruff/mypy, align release c…
EshwarCVS Apr 14, 2026
9cd399d
ci: remove dev branch, make Codecov non-blocking
EshwarCVS Apr 15, 2026
83e7330
ci: remove dev branch trigger, make Codecov non-blocking
EshwarCVS Apr 15, 2026
35060f6
ci: sync final 2-branch workflow files to feature branch
EshwarCVS Apr 15, 2026
409f591
docs(bench): sync README benchmark snapshot [skip ci]
github-actions[bot] Apr 15, 2026
170ff8d
Merge pull request #19 from FasterApiWeb/stage
EshwarCVS Apr 17, 2026
c94aaa6
docs(bench): sync README benchmark snapshot [skip ci]
github-actions[bot] Apr 17, 2026
c53faf3
docs: comprehensive documentation covering all gap-analysis items (#21)
EshwarCVS Apr 17, 2026
6dbf8b2
chore: mark branch for deletion — superseded by master
EshwarCVS Apr 17, 2026
4a5e366
docs(bench): sync README benchmark snapshot [skip ci]
github-actions[bot] Apr 17, 2026
0f98899
docs(bench): sync README benchmark snapshot [skip ci]
github-actions[bot] Apr 17, 2026
532f1e2
Merge pull request #22 from FasterApiWeb/claude/setup-cicd-release-wo…
EshwarCVS Apr 17, 2026
f0a81c6
docs(bench): sync README benchmark snapshot [skip ci]
github-actions[bot] Apr 17, 2026
bbae612
chore: update author metadata and align CONTRIBUTING with two-branch …
EshwarCVS Apr 17, 2026
b46b824
Merge branch 'master' into claude/update-author-merge-1LJnh
EshwarCVS Apr 17, 2026
191ef56
fix(response): remove redundant cast in JSONResponse._render
EshwarCVS Apr 17, 2026
9b38967
ci(bench): lower regression floors for CI runner variance
EshwarCVS Apr 17, 2026
18efb7d
Merge branch 'master' into claude/update-author-merge-1LJnh
EshwarCVS Apr 17, 2026
1206d72
fix(response): remove redundant cast in JSONResponse._render (re-appl…
EshwarCVS Apr 17, 2026
23c4466
fix(response): remove redundant cast in JSONResponse._render
EshwarCVS Apr 17, 2026
138ae11
docs(bench): sync README benchmark snapshot [skip ci]
github-actions[bot] Apr 17, 2026
1236178
chore: merge master — keep latest benchmark snapshot in README
EshwarCVS Apr 17, 2026
b80cfb2
chore: merge master benchmark snapshot
EshwarCVS Apr 17, 2026
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
1 change: 1 addition & 0 deletions .branch-cleanup
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This branch has been superseded by master and can be safely deleted.
7 changes: 4 additions & 3 deletions .github/workflows/auto-tag-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@ name: Auto Tag Release
on:
pull_request:
types: [closed]
branches: [master, stage, dev]
branches: [master, stage]

permissions:
contents: write

jobs:
tag:
# Always fire on master merges (defaults to patch bump).
# Fire on stage only when a release:* label is present.
if: >
github.event.pull_request.merged == true &&
(
github.event.pull_request.base.ref == 'master' ||
contains(github.event.pull_request.labels.*.name, 'release:patch') ||
contains(github.event.pull_request.labels.*.name, 'release:minor') ||
contains(github.event.pull_request.labels.*.name, 'release:major')
Expand Down Expand Up @@ -40,8 +43,6 @@ jobs:
TARGET_BRANCH="${{ github.event.pull_request.base.ref }}"
if [ "$TARGET_BRANCH" = "stage" ]; then
BASE_PREFIX="stage-v"
elif [ "$TARGET_BRANCH" = "dev" ]; then
BASE_PREFIX="dev-v"
fi

LAST_TAG="$(git tag -l "${BASE_PREFIX}*" --sort=-v:refname | sed -n '1p')"
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: CI

on:
push:
branches: [master, stage, dev]
branches: [master, stage]
pull_request:
branches: [master, stage, dev]
branches: [master, stage]

permissions:
contents: read
Expand Down Expand Up @@ -50,6 +50,6 @@ jobs:
with:
files: coverage.xml
flags: python${{ matrix.python-version }}
fail_ci_if_error: true
fail_ci_if_error: false
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
103 changes: 6 additions & 97 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ name: Release
on:
push:
branches:
- master
- stage
- dev
tags:
- "v*"

Expand All @@ -15,56 +13,12 @@ permissions:
packages: write

jobs:
# ── Publish channel builds to TestPyPI on branch merges ───────────────
# ── Publish pre-release builds to TestPyPI on stage merges ───────────
# Channel mapping:
# - dev -> 0.0.0.devN
# - stage -> 0.0.0aN
# - master -> 0.0.0rcN
# Stable PyPI releases remain tag-based (vX.Y.Z).
publish-testpypi-dev:
if: github.ref == 'refs/heads/dev'
runs-on: ubuntu-latest
environment: testpypi
permissions:
contents: read
steps:
- name: Verify TestPyPI token is configured
run: |
if [ -z "${{ secrets.TEST_PYPI_API_TOKEN }}" ]; then
echo "::error::Missing secret TEST_PYPI_API_TOKEN (Settings → Secrets and variables → Actions)."
exit 1
fi

- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: actions/setup-python@v5
with:
python-version: "3.13"

- name: Install build tools
run: pip install build

- name: Build channel package (dev)
env:
SETUPTOOLS_SCM_PRETEND_VERSION: 0.0.0.dev${{ github.run_number }}
run: python -m build

- name: Upload package artifact (dev channel)
uses: actions/upload-artifact@v4
with:
name: dist-dev
path: dist/

- name: Publish to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
user: __token__
password: ${{ secrets.TEST_PYPI_API_TOKEN }}
skip-existing: true

# - stage -> 0.0.0aN (TestPyPI pre-release)
# - master -> vX.Y.Z (PyPI stable, via auto-tag from auto-tag-release.yml)
# Stable PyPI releases are tag-based (vX.Y.Z); master branch push triggers
# the auto-tag workflow which creates the tag that drives this pipeline.
publish-testpypi-stage:
if: github.ref == 'refs/heads/stage'
runs-on: ubuntu-latest
Expand Down Expand Up @@ -109,51 +63,6 @@ jobs:
password: ${{ secrets.TEST_PYPI_API_TOKEN }}
skip-existing: true

publish-testpypi-master:
if: github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
environment: testpypi
permissions:
contents: read
steps:
- name: Verify TestPyPI token is configured
run: |
if [ -z "${{ secrets.TEST_PYPI_API_TOKEN }}" ]; then
echo "::error::Missing secret TEST_PYPI_API_TOKEN (Settings → Secrets and variables → Actions)."
echo "Create an API token on TestPyPI for the `faster-api-web` project and add it as TEST_PYPI_API_TOKEN."
exit 1
fi

- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: actions/setup-python@v5
with:
python-version: "3.13"

- name: Install build tools
run: pip install build

- name: Build channel package (master)
env:
SETUPTOOLS_SCM_PRETEND_VERSION: 0.0.0rc${{ github.run_number }}
run: python -m build

- name: Upload package artifact (master channel)
uses: actions/upload-artifact@v4
with:
name: dist-master
path: dist/

- name: Publish to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
user: __token__
password: ${{ secrets.TEST_PYPI_API_TOKEN }}
skip-existing: true

# ── Gate: only release from master ──────────────────────────────────
check-branch:
if: startsWith(github.ref, 'refs/tags/v')
Expand Down Expand Up @@ -277,7 +186,7 @@ jobs:

# ── Step 5: Create GitHub Release with artifacts ───────────────────
github-release:
needs: [build, publish-pypi, publish-docker]
needs: [build, publish-pypi]
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
permissions:
Expand Down
35 changes: 12 additions & 23 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,42 +7,38 @@ Thank you for your interest in contributing! This document explains how we work.
## Branch Model

```
dev/your-feature ──PR──▶ stage ──PR──▶ master
(yours) (integration) (production)
feature/your-feature ──PR──▶ stage ──PR──▶ master
(yours) (integration) (production)
```

| Branch | Purpose | Who can push directly |
|---|---|---|
| `master` | Production-ready code, releases | **Nobody** — merge from `stage` via PR only |
| `stage` | Integration / pre-release | **Nobody** — merge from `dev` via PR only |
| `dev` / `dev/*` | Feature integration and branch-level previews | Maintainers via PR only |
| `stage` | Integration / pre-release | **Nobody** — merge from feature branches via PR only |

For **security-sensitive** reports, use the process in [SECURITY.md](SECURITY.md) instead of a public issue.

### Rules

1. **Never push directly to `master`, `stage`, or `dev`.**
1. **Never push directly to `master` or `stage`.**
2. Create your branch from **`stage`** (never from an outdated `master` without syncing):
```bash
git checkout stage
git pull origin stage
git checkout -b dev/my-feature
git checkout -b feature/my-feature
```
3. Commit with **clear messages** (what changed and why in one line; optional scope prefix, e.g. `docs:`, `bench:`).
4. Open a **pull request from your branch → `dev`** for first integration.
4. Open a **pull request from your branch → `stage`** for integration.
5. CI (tests on Python 3.10–3.13 + benchmarks on PRs) must pass.
6. At least **one approval** is required before merging to `stage`, when reviewers are available.
7. Periodically, maintainers promote `dev` → `stage` and `stage` → `master`.
7. Periodically, maintainers promote `stage` → `master`.
8. **Stable releases** are **git tags** on `master` (`v0.2.0`, …), which trigger PyPI + Docker + GitHub Releases. The **PyPI version is taken from the tag** (see `hatch-vcs` in `pyproject.toml`) — **do not** rely on editing a static `version =` in `pyproject.toml` for releases.
9. To automate semver tagging, add exactly one PR label: `release:patch`, `release:minor`, or `release:major`. On merge:
- to `master`: creates `vX.Y.Z`
- to `stage`: creates `stage-vX.Y.Z`
- to `dev`: creates `dev-vX.Y.Z`
10. Channel builds publish automatically:
- `dev` push: TestPyPI `0.0.0.devN`
- `stage` push: TestPyPI `0.0.0aN`
- `master` push: TestPyPI `0.0.0rcN`
- `vX.Y.Z` tag: stable PyPI + Docker + GitHub Release
- `stage` push: TestPyPI `0.0.0aN`
- `vX.Y.Z` tag: stable PyPI + Docker + GitHub Release

---

Expand Down Expand Up @@ -87,7 +83,7 @@ Before opening a PR, verify:

## What Happens on Your PR

When you open a PR to `dev`, `stage`, or `master`, two workflows run automatically:
When you open a PR to `stage` or `master`, two workflows run automatically:

1. **CI** — Tests on Python 3.10, 3.11, 3.12, 3.13 with coverage
2. **Benchmark** — Runs framework benchmarks and posts a comparison comment on the PR
Expand Down Expand Up @@ -116,12 +112,6 @@ Configure these in **Settings → Rules → Rulesets**:
- Block direct pushes
- Require pull request
- Require status checks: `CI`, `Benchmark`
- Restrict allowed source branch for PRs to `dev` only
3. **Dev ruleset**
- Target: `dev`
- Block direct pushes
- Require pull request (even for maintainers, if desired)
- Require status checks: `CI`, `Benchmark`

Also set **Settings → Actions → General** so workflows can create commits/tags when needed (`Read and write permissions` for `GITHUB_TOKEN`).

Expand Down Expand Up @@ -149,11 +139,10 @@ Also set **Settings → Actions → General** so workflows can create commits/ta
### Notes about branch channel versions

- TestPyPI/PyPI require valid PEP 440 versions.
- Human-readable suffixes like `-stage` or `-dev` are not valid upload versions on PyPI.
- Human-readable suffixes like `-stage` are not valid upload versions on PyPI.
- Channel identity is represented with valid semver segments:
- `dev` channel: `.devN`
- `stage` channel: `aN`
- `master` preview channel: `rcN`
- stable: `vX.Y.Z` (tag-based)

---

Expand Down
1 change: 1 addition & 0 deletions FasterAPI/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from ._version import get_version

__version__ = get_version()
__author__ = "Eshwar Chandra Vidhyasagar Thedla"

from .app import Faster
from .background import BackgroundTask, BackgroundTasks
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -490,13 +490,13 @@ uvicorn examples.full_crud_app:app --reload

| Endpoint | FasterAPI | FastAPI | Speedup |
|---|---|---|---|
| `GET /health` | **467 req/s** | 514 req/s | **0.91x** |
| `GET /users/{id}` | **512 req/s** | 549 req/s | **0.93x** |
| `POST /users` | **464 req/s** | 473 req/s | **0.98x** |
| `GET /health` | **478 req/s** | 490 req/s | **0.98x** |
| `GET /users/{id}` | **478 req/s** | 535 req/s | **0.89x** |
| `POST /users` | **452 req/s** | 466 req/s | **0.97x** |

| Routing | Radix ops/s | Regex ops/s | Speedup |
|---|---|---|---|
| 100-route lookup | **971,712** | 100,759 | **9.6x** |
| 100-route lookup | **944,886** | 94,498 | **10.0x** |

_This block is updated automatically on pushes to `dev`, `stage`, and `master`._

Expand Down
8 changes: 4 additions & 4 deletions benchmarks/baseline.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"_comment": "CI fails if measured speedups drop below these floors (Ubuntu runner). Update when intentional perf work lands.",
"min_speedup_vs_fastapi": {
"health": 3.8,
"users_get": 4.5,
"users_post": 3.8
"health": 3.5,
"users_get": 4.0,
"users_post": 3.5
},
"min_radix_speedup_vs_regex": 4.0
"min_radix_speedup_vs_regex": 3.5
}
1 change: 0 additions & 1 deletion benchmarks/update_readme_benchmarks.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import re
from pathlib import Path


README_PATH = Path("README.md")
BENCH_JSON = Path("bench_results.json")
ROUTING_JSON = Path("routing_results.json")
Expand Down
Loading
Loading