Skip to content
Open
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
19 changes: 11 additions & 8 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,26 @@ jobs:
with:
fetch-depth: 0

- uses: actions/setup-python@v5
- name: Install uv
uses: astral-sh/setup-uv@v4
with:
python-version: 3.13
version: "latest"
enable-cache: true

- run: |
pip install --upgrade pip
pip install hatch
hatch build
- name: Set up Python
run: uv python install 3.13

- name: Build package
run: uv build

- uses: actions/upload-artifact@v4
with:
path: ./dist
if-no-files-found: error

pypi-publish:
needs: ['build']
environment: 'publish'
needs: ["build"]
environment: "publish"

name: upload release to PyPI
runs-on: ubuntu-latest
Expand Down
117 changes: 69 additions & 48 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,63 +16,84 @@ jobs:

steps:
- uses: actions/checkout@v5
- name: Set up Python
uses: actions/setup-python@v5
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
python-version: ${{ matrix.python-version }}
- uses: actions/cache@v4
id: cache
with:
path: ${{ env.pythonLocation }}
key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-test

- name: Install dependencies
if: steps.cache-python-env.outputs.cache-hit != 'true'
run: |
pip install --upgrade pip
pip install -e .[dev]
run: uv sync --extra dev

- name: Lint
run: |
make lint
- name: Install app

- name: Add uv venv to PATH
run: |
pip install -e .
echo "$PWD/.venv/bin" >> $GITHUB_PATH
cd ../..
- name: Create project using default template
run: |
fastapi-gen hello_world
cd hello_world
make lint
make test
cd ..
- name: Create project using "hello_world" template
run: |
fastapi-gen hello_world_v2 --template hello_world
cd hello_world_v2
make lint
make test
cd ..
- name: Create project using "advanced" template
run: |
fastapi-gen advanced --template advanced
cd advanced
make lint
make test
cd ..
- name: Create project using "nlp" template
run: |
fastapi-gen nlp --template nlp
cd nlp
make lint
make test
cd ..
- name: Create project using "langchain" template

# - name: Create project using default template
# run: |
# fastapi-gen hello_world
# cd hello_world
# make lint
# make test
# cd ..

# - name: Create project using "hello_world" template
# run: |
# fastapi-gen hello_world_v2 --template hello_world
# cd hello_world_v2
# make lint
# make test
# cd ..

# - name: Create project using "advanced" template
# run: |
# fastapi-gen advanced --template advanced
# cd advanced
# make lint
# make test
# cd ..

# - name: Create project using "nlp" template
# run: |
# fastapi-gen nlp --template nlp
# cd nlp
# make lint
# make test
# cd ..

# - name: Create project using "langchain" template
# run: |
# fastapi-gen langchain_app --template langchain
# cd langchain_app
# make lint
# make test
# cd ..

- name: Install dependencies (Linux)
if: runner.os == 'Linux'
run: |
fastapi-gen langchain_app --template langchain
cd langchain_app
make lint
make test
cd ..
- name: Create project using "llama" template
sudo apt-get update
sudo apt-get install -y cmake ninja-build build-essential libopenblas-dev

# - name: Install llama-cpp-python (Linux)
# if: runner.os == 'Linux'
# run: |
# CMAKE_ARGS="-DGGML_BLAS=OFF -DLLAMA_CUBLAS=OFF" \
# FORCE_CMAKE=1 \
# pip install llama-cpp-python

# - name: Install llama-cpp-python (macOS)
# if: runner.os == 'macOS'
# run: |
# CMAKE_ARGS="-DLLAMA_METAL=OFF -DGGML_METAL=OFF -DGGML_BLAS=OFF -DGGML_CUBLAS=OFF -DGGML_OPENMP=OFF -DGGML_NATIVE=OFF" \
# FORCE_CMAKE=1 \
# pip install --force-reinstall --no-cache-dir llama-cpp-python

- name: Install llama-cpp-python
run: |
fastapi-gen llama_app --template llama
cd llama_app
Expand Down
14 changes: 14 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
include README.md
include LICENSE
recursive-include src/templates *.toml
recursive-include src/templates *.txt
recursive-include src/templates *.py
recursive-include src/templates *.md
recursive-include src/templates Makefile
recursive-include src/templates .env*
recursive-include src/templates .git*
graft src/templates
prune src/templates/__pycache__
prune src/templates/.pytest_cache
prune src/templates/venv
prune src/templates/.ruff_cache
26 changes: 23 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,27 @@
.PHONY: lint lint-fix
.PHONY: install sync lint lint-fix test build clean update

install:
uv sync --extra dev

sync:
uv sync --extra dev

lint:
ruff check src/cli
uv run ruff check .
uv run ruff format --check --diff .

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

test:
uv run pytest

build:
uv build

clean:
rm -rf dist/ build/ *.egg-info/ .pytest_cache/ .ruff_cache/

update:
uv sync --upgrade
27 changes: 15 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,18 @@ cd my_app && make start

> **Platform Support:** Works on macOS and Linux | [Report Issues](https://github.com/mirpo/fastapi-gen/issues/new)

## Requirements

- Python 3.12+
- `uv` (`curl -LsSf https://astral.sh/uv/install.sh | sh`)

## Why FastAPI Gen?

<div align="center">

| **Focus on Code** | **Production Ready** | **Testing Included** | **Zero Config** |
|:---:|:---:|:---:|:---:|
| Skip boilerplate setup | Enterprise patterns | Real test coverage | Ready-to-run templates |
| **Focus on Code** | **Production Ready** | **Testing Included** | **Zero Config** |
| :--------------------: | :------------------: | :------------------: | :--------------------: |
| Skip boilerplate setup | Enterprise patterns | Real test coverage | Ready-to-run templates |

</div>

Expand Down Expand Up @@ -160,13 +165,13 @@ cd my_app && make start

## Template Comparison

| Template | Best For | Complexity | AI/ML | Database | Auth |
|----------|----------|------------|--------|----------|------|
| **Hello World** | Learning, Simple APIs | ⭐ | ❌ | ❌ | ❌ |
| **Advanced** | Production Apps | ⭐⭐⭐ | ❌ | ✅ | ✅ |
| **NLP** | AI Text Processing | ⭐⭐⭐⭐ | ✅ | ❌ | ❌ |
| **LangChain** | LLM Workflows | ⭐⭐⭐⭐ | ✅ | ❌ | ❌ |
| **Llama** | Local LLM | ⭐⭐⭐⭐⭐ | ✅ | ❌ | ❌ |
| Template | Best For | Complexity | AI/ML | Database | Auth |
| --------------- | --------------------- | ---------- | ----- | -------- | ---- |
| **Hello World** | Learning, Simple APIs | ⭐ | ❌ | ❌ | ❌ |
| **Advanced** | Production Apps | ⭐⭐⭐ | ❌ | ✅ | ✅ |
| **NLP** | AI Text Processing | ⭐⭐⭐⭐ | ✅ | ❌ | ❌ |
| **LangChain** | LLM Workflows | ⭐⭐⭐⭐ | ✅ | ❌ | ❌ |
| **Llama** | Local LLM | ⭐⭐⭐⭐⭐ | ✅ | ❌ | ❌ |

## What You Get Out of the Box

Expand Down Expand Up @@ -196,8 +201,6 @@ make start # Run!

## Creating an App

**You'll need to have Python 3.12+ or later version on your local development machine**. We recommend using the latest LTS version. You can use [pyenv](https://github.com/pyenv/pyenv) (macOS/Linux) to switch Python versions between different projects.

### Basic template

```console
Expand Down
31 changes: 15 additions & 16 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
requires = ["setuptools>=45", "wheel"]
build-backend = "setuptools.build_meta"

[tool.hatch.build]
include = ["src"]
[tool.setuptools]
include-package-data = true

[tool.hatch.build.targets.wheel]
sources = ["src"]
[tool.setuptools.packages.find]
where = ["src"]

[tool.setuptools.package-data]
"*" = ["*"]

[project]
name = "fastapi-gen"
dynamic = ["version"]
version = "0.9.0"
description = "Set up a modern REST API by running one command."
readme = "README.md"
requires-python = ">=3.12"
Expand Down Expand Up @@ -51,7 +54,6 @@ classifiers = [
"Framework :: Pydantic",
"Framework :: Pydantic :: 1",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
Expand All @@ -72,14 +74,11 @@ Source = "https://github.com/mirpo/fastapi-gen"
[project.scripts]
fastapi-gen = "cli.__main__:main"

[tool.hatch.version]
path = "src/cli/__about__.py"

[tool.hatch.envs.default]
dependencies = ["coverage[toml]==7.10.3", "pytest"]

[[tool.hatch.envs.all.matrix]]
python = ["3.12", "3.13"]
[dependency-groups]
dev = [
"coverage[toml]==7.10.3",
"pytest",
]

[tool.black]
target-version = ["py312"]
Expand Down
27 changes: 20 additions & 7 deletions src/cli/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,25 +27,38 @@ def replace_string_in_file(file_path, old_string, new_string):

_choices = ["hello_world", "advanced", "nlp", "langchain", "llama"]


def _create_dir(path: str):
try:
os.makedirs(path)
click.echo(f"Folder '{path}' created successfully.")
except OSError as e:
click.echo(f"Error creating folder: {e}")


def _walk_resources(package: str, path: str = ""):
for file in resources.files(package).iterdir():
if file.name in ["__pycache__", ".pytest_cache", "venv"]:
if (
file.name in ["__pycache__", ".pytest_cache", "venv", ".ruff_cache", ".venv", ".git"]
or file.name.endswith(".egg-info")
or file.name.endswith("__pycache__")
or not file.name
or file.name.strip() == ""
or file.name.isspace()
):
continue

if file.is_dir():
_create_dir(os.path.join(path, file.name))
_walk_resources(f"{package}.{file.name}", os.path.join(path, file.name))
elif file.name.startswith("."):
with open(os.path.join(path, file.name), "w") as f:
f.write(file.read_text())
else:
with open(os.path.join(path, file.name), "w") as f:
f.write(resources.read_text(package, file.name))


@click.command()
@click.option("-t", "--template", default="hello_world", type=click.Choice(_choices), help="template")
@click.argument("name")
Expand Down Expand Up @@ -82,21 +95,21 @@ def fastapi_create(name: str, template: str):
Success! Created new-app at {new_project_path}
Inside that directory, you can run several commands:

{click.style('make start', bg='blue', fg='white')}
{click.style("make start", bg="blue", fg="white")}
Starts the development server.

{click.style('make test', bg='blue', fg='white')}
{click.style("make test", bg="blue", fg="white")}
Starts the test runner.

{click.style('make lint', bg='blue', fg='white')}
{click.style("make lint", bg="blue", fg="white")}
Starts linters.

We suggest that you begin by typing:

{click.style(f'cd {name}', bg='blue', fg='white')}
{click.style('make start', bg='blue', fg='white')}
{click.style(f"cd {name}", bg="blue", fg="white")}
{click.style("make start", bg="blue", fg="white")}

{click.style('Happy hacking!', blink=True, bold=True)}
{click.style("Happy hacking!", blink=True, bold=True)}
"""
click.echo(welcome_message)

Expand Down
Loading
Loading