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
2 changes: 1 addition & 1 deletion .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ jobs:
node-version: ${{ env.NODE_VERSION }}
cache: "npm"
# Need uv too — pages.ts imports ./modules.generated, which is
# produced by `sm gen-pages` from the installed Python modules.
# produced by `smpy gen-pages` from the installed Python modules.
- uses: astral-sh/setup-uv@v8.0.0
with:
enable-cache: true
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Initial public release. All 12 Python packages publish to PyPI and all 3 JS pack

### Added

- `sm new <app>` CLI generator (shipped via the `simple_module_cli` PyPI distribution) scaffolding a working app with `users + dashboard + permissions` pre-wired.
- `smpy new <app>` CLI generator (shipped via the `simple_module_cli` PyPI distribution) scaffolding a working app with `users + dashboard + permissions` pre-wired.
- PyPI Trusted Publishing workflow (`.github/workflows/release.yml`) for zero-secret releases.
- npm Trusted Publishing for all three JS packages.

Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ dev-ui:

# Regenerate host/client_app/modules.{manifest.json,generated.ts,generated.css} from installed modules.
gen-pages:
uv run --project host sm host gen-pages --host-dir=host/client_app
uv run --project host smpy host gen-pages --host-dir=host/client_app

# Install JS deps declared by installed modules into host/client_app/node_modules.
# Wheel-installed modules need this; in-repo workspace modules do not.
sync-module-deps:
uv run --project host sm host sync-js-deps --host-client-app=host/client_app
uv run --project host smpy host sync-js-deps --host-client-app=host/client_app

# Build
build:
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ A modular-monolith framework for Python. Each feature lives in its own self-cont
If you want to **build an app on simple_module**, not hack on the framework itself:

```bash
uvx --from simple_module_cli sm new my-app
uvx --from simple_module_cli smpy new my-app
cd my-app
make dev
```
Expand Down Expand Up @@ -45,7 +45,7 @@ make migrate
make dev
```

Hit `http://localhost:8000` — you land on the public page. `/users/login` is the email+password login, `/dashboard/` is the authenticated home, and `/dashboard/doctor` is the admin-only "sm doctor" panel (static checks, migrations, dev server, modules).
Hit `http://localhost:8000` — you land on the public page. `/users/login` is the email+password login, `/dashboard/` is the authenticated home, and `/dashboard/doctor` is the admin-only "smpy doctor" panel (static checks, migrations, dev server, modules).

## Create a new module

Expand Down Expand Up @@ -121,7 +121,7 @@ Power users can still override the following bootstrap knobs via env if needed:
All module-level settings — users, SMTP, Celery broker, file storage backend, etc. — live in the admin UI. After upgrading an existing deployment, run once:

```bash
uv run sm settings import-from-env
uv run smpy settings import-from-env
```

to seed DB overrides from the current `SM_*` environment.
Expand Down Expand Up @@ -163,7 +163,7 @@ The 300-line file cap (enforced by CI) usually pushes you to factor row-level co
Either use the CLI:

```bash
uv run sm users create-admin --email admin@example.com --password changeme
uv run smpy users create-admin --email admin@example.com --password changeme
```

Or let the app bootstrap it automatically on first boot by setting env vars **before** running `make migrate && make dev`:
Expand Down
4 changes: 2 additions & 2 deletions docs/database/migrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ uv run alembic downgrade orders@base # back to the state before the orders m

## First migration of a new module

When you scaffold a module with `sm create-module`, the *first* autogenerate revision produces a file that needs this marker added by hand:
When you scaffold a module with `smpy create-module`, the *first* autogenerate revision produces a file that needs this marker added by hand:

```python
# migrations/versions/XXXX_add_orders_tables.py
Expand Down Expand Up @@ -108,7 +108,7 @@ If `invoices` has an FK to `orders.order.id`:

- Alembic will emit `ADD CONSTRAINT` in the invoices table's migration.
- The migration that creates `invoices_invoice` must come **after** the one that creates `orders_order` in linear history.
- `sm create-module` + `uv run alembic revision --autogenerate` handle this naturally as long as `depends_on` is correct in `ModuleMeta`.
- `smpy create-module` + `uv run alembic revision --autogenerate` handle this naturally as long as `depends_on` is correct in `ModuleMeta`.

On Postgres, cross-schema FKs work natively (`orders.order.id ← invoices.invoice.order_id`).

Expand Down
4 changes: 2 additions & 2 deletions docs/e2e-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ make dev # FastAPI on :8000 + Vite on :5050
Create the first admin user (needed for e2e auth):

```bash
uv run sm-users create-admin --email admin@example.com --password admin
uv run smpy users create-admin --email admin@example.com --password admin
```

Or set `SM_USERS_BOOTSTRAP_EMAIL` / `SM_USERS_BOOTSTRAP_PASSWORD` in `.env`
Expand All @@ -48,7 +48,7 @@ When you write e2e tests, read these environment variables (all optional):
| Variable | Default | Notes |
| -------------- | ------------------------- | ------------------------------------------------------------ |
| `E2E_BASE_URL` | `http://localhost:8000` | Where the FastAPI host is listening. |
| `E2E_USERNAME` | `admin@example.com` | Email of the admin user created via `sm-users create-admin`. |
| `E2E_USERNAME` | `admin@example.com` | Email of the admin user created via `smpy users create-admin`. |
| `E2E_PASSWORD` | `admin` | Password of the above admin user. |
| `SM_USERS_VERIFICATION_TOKEN_SECRET` | `dev-verify-token-secret-change-me` | Must match the running server's value so locally-minted invite tokens are accepted. |

Expand Down
4 changes: 2 additions & 2 deletions docs/framework-conventions.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ modules/<name>/
└── pages/ # *.tsx — auto-discovered by Vite
```

Scaffold a fresh module with `sm create-module <name> --dest modules/<name>` — it generates all of the above. Then run `uv add ./modules/<name>` to register it on your app.
Scaffold a fresh module with `smpy create-module <name> --dest modules/<name>` — it generates all of the above. Then run `uv add ./modules/<name>` to register it on your app.

## ModuleMeta

Expand Down Expand Up @@ -296,7 +296,7 @@ class OrdersModule(ModuleBase):
return {"orders": Path(str(importlib.resources.files(__package__) / "locales"))}
```

`sm create-module` scaffolds this method and a matching `locales/en.json` automatically.
`smpy create-module` scaffolds this method and a matching `locales/en.json` automatically.

### Key naming

Expand Down
2 changes: 1 addition & 1 deletion docs/framework/i18n.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class OrdersModule(ModuleBase):
}
```

The key (`"orders"`) is the **namespace** — it prefixes every key in the files. `sm create-module` scaffolds this method and a starter `en.json` automatically.
The key (`"orders"`) is the **namespace** — it prefixes every key in the files. `smpy create-module` scaffolds this method and a starter `en.json` automatically.

## Key naming

Expand Down
2 changes: 1 addition & 1 deletion docs/framework/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ Values are keyed by `<namespace>.<key>`. Conventional namespace is the module na
Existing deployments that used env vars for module settings should run once:

```bash
uv run sm-settings import-from-env
uv run smpy settings import-from-env
```

This reads the current environment, looks up keys the settings service knows about, and writes overrides into the DB. Idempotent — skips keys that already have a DB override. After running, you can remove those env vars from deployment config.
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ Prefix `SM_BG_TASKS_*`. The defaults in `docker-compose.yml` already set these s
After upgrading from an older deployment, import existing `SM_*` values into the DB settings store once:

```bash
uv run sm-settings import-from-env
uv run smpy settings import-from-env
```

This is idempotent — it only seeds keys that don't have a DB override yet.
Expand Down
6 changes: 3 additions & 3 deletions docs/guide/first-module.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Your first module

A stage-by-stage walk-through: from `sm create-module` to a working Orders module with custom fields, validation, a menu entry, and a test.
A stage-by-stage walk-through: from `smpy create-module` to a working Orders module with custom fields, validation, a menu entry, and a test.

Assumes you've completed the [Quickstart](/guide/quickstart) and have an app on disk created by `sm new`.
Assumes you've completed the [Quickstart](/guide/quickstart) and have an app on disk created by `smpy new`.

## 1. Scaffold

```bash
sm create-module orders --dest modules/orders
smpy create-module orders --dest modules/orders
uv add ./modules/orders
```

Expand Down
24 changes: 12 additions & 12 deletions docs/guide/installation.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Installation

You install simple_module_python by installing its **CLI** — `sm` — and using it to scaffold a new app. There's no repo to clone; the framework ships as a set of Python packages on PyPI and the CLI assembles them into a working project for you.
You install simple_module_python by installing its **CLI** — `smpy` — and using it to scaffold a new app. There's no repo to clone; the framework ships as a set of Python packages on PyPI and the CLI assembles them into a working project for you.

## Prerequisites

Expand All @@ -26,28 +26,28 @@ If you prefer pipx:
pipx install simple_module_cli
```

That puts `sm` on your PATH globally. Confirm with:
That puts `smpy` on your PATH globally. Confirm with:

```bash
sm --help
smpy --help
```

## Scaffold a new app

```bash
sm new myapp
smpy new myapp
```

Interactive — you pick the database (SQLite / Postgres), whether to enable multi-tenancy, and which bundled modules to include. Skip the prompts and accept the defaults with:

```bash
sm new myapp --yes
smpy new myapp --yes
```

Or pick a preset and add modules non-interactively:

```bash
sm new myapp --preset standard --with background_tasks,file_storage
smpy new myapp --preset standard --with background_tasks,file_storage
```

| Preset | Modules |
Expand All @@ -56,7 +56,7 @@ sm new myapp --preset standard --with background_tasks,file_storage
| `standard` (default) | `minimal` + `dashboard`, `settings`, `feature_flags` |
| `full` | `standard` + `background_tasks`, `file_storage` |

After scaffolding, `sm new` runs `uv sync`, `npm install`, and `alembic upgrade head` for you (skip with `--no-install` if you'd rather drive that yourself).
After scaffolding, `smpy new` runs `uv sync`, `npm install`, and `alembic upgrade head` for you (skip with `--no-install` if you'd rather drive that yourself).

## Boot it

Expand All @@ -74,7 +74,7 @@ Hit `http://localhost:8000`. You should see the landing page.

## Database choice

`sm new` writes a `.env.example`. The default is SQLite (zero setup):
`smpy new` writes a `.env.example`. The default is SQLite (zero setup):

```bash
SM_DATABASE_URL=sqlite+aiosqlite:///./app.db
Expand All @@ -100,7 +100,7 @@ See [Configuration](/guide/configuration) for the full list of env vars.
If you included the `users` module:

```bash
uv run sm-users create-admin --email admin@example.com --password changeme
uv run smpy users create-admin --email admin@example.com --password changeme
```

Or set bootstrap env vars so the admin is auto-created on first boot:
Expand All @@ -116,7 +116,7 @@ Then `make migrate && make dev`.
## Add a module to your app

```bash
sm create-module orders --dest modules/orders
smpy create-module orders --dest modules/orders
```

That generates `modules/orders/` with the full layout (model, contracts, service, endpoints, pages, tests, locales, `pyproject.toml` entry point). Add the package to your app's dependencies and re-sync:
Expand All @@ -133,7 +133,7 @@ The full walkthrough is in [Your first module](/guide/first-module).
When new releases of `simple_module_*` ship to PyPI, bump every dep in lockstep:

```bash
sm package-update
smpy package-update
```

Pass `--dry-run` first to preview the diff.
Expand All @@ -155,6 +155,6 @@ Re-run `uv sync` — entry points are registered at install time, not at import
## Next steps

- [Quickstart](/guide/quickstart) — bootstrap and tour the running app in five minutes.
- [Project structure](/guide/project-structure) — what `sm new` lays down.
- [Project structure](/guide/project-structure) — what `smpy new` lays down.
- [Your first module](/guide/first-module) — extend the app with your own domain logic.
- [Bundled modules](/modules/) — what each pre-installed module ships.
12 changes: 6 additions & 6 deletions docs/guide/project-structure.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Project structure

`sm new myapp` lays down a focused tree. The host owns the runnable app; modules live as packages under `modules/`; everything else is configuration.
`smpy new myapp` lays down a focused tree. The host owns the runnable app; modules live as packages under `modules/`; everything else is configuration.

```text
myapp/
Expand All @@ -25,19 +25,19 @@ myapp/
│ ├── env.py # calls build_module_metadata() to union every module's MetaData
│ └── versions/
├── modules/ # your custom modules go here (sm create-module --dest modules/<name>)
│ └── orders/ # example — generated by sm create-module orders
├── modules/ # your custom modules go here (smpy create-module --dest modules/<name>)
│ └── orders/ # example — generated by smpy create-module orders
│ └── … (see "Anatomy of a module" below)
└── tests/ # your app-level tests
└── test_smoke.py
```

`sm new --flat` skips the `modules/` directory entirely, for the case where the app only consumes published modules and never authors its own. `sm new --preset minimal` ships fewer pre-installed modules.
`smpy new --flat` skips the `modules/` directory entirely, for the case where the app only consumes published modules and never authors its own. `smpy new --preset minimal` ships fewer pre-installed modules.

## Bundled modules

The framework's first-party modules live on PyPI as `simple_module_<name>` and are added to `pyproject.toml` by `sm new` based on the preset / `--with` you pick:
The framework's first-party modules live on PyPI as `simple_module_<name>` and are added to `pyproject.toml` by `smpy new` based on the preset / `--with` you pick:

| Module | What it provides |
|---|---|
Expand Down Expand Up @@ -93,7 +93,7 @@ See the [module authoring guide](/module-authoring) for the full contract.
| `client_app/modules.manifest.json` | `make gen-pages` | Same |
| `client_app/modules.generated.css` | `make gen-pages` | Same |
| `migrations/versions/XXXX_*.py` | `uv run alembic revision --autogenerate -m "…"` | When you add/change SQLModel tables |
| `modules/<name>/**` | `sm create-module <name> --dest modules/<name>` | Scaffolding a new module |
| `modules/<name>/**` | `smpy create-module <name> --dest modules/<name>` | Scaffolding a new module |

Never hand-edit the `.generated.*` files — they are overwritten on the next `make gen-pages` run.

Expand Down
12 changes: 6 additions & 6 deletions docs/guide/quickstart.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
# Quickstart

Five minutes from `sm new` to a running app with a freshly scaffolded module.
Five minutes from `smpy new` to a running app with a freshly scaffolded module.

## 1. Install the CLI

```bash
uv tool install simple_module_cli
```

(Or `pipx install simple_module_cli`.) That puts `sm` on your PATH globally.
(Or `pipx install simple_module_cli`.) That puts `smpy` on your PATH globally.

## 2. Scaffold an app

```bash
sm new myapp --yes
smpy new myapp --yes
cd myapp
```

`--yes` accepts the defaults (SQLite, no multi-tenancy, the `standard` preset: `auth`, `users`, `permissions`, `dashboard`, `settings`, `feature_flags`). The CLI runs `uv sync`, `npm install`, and `alembic upgrade head` for you.

For an interactive run with prompts, drop the `--yes`. For a preset + extras: `sm new myapp --preset standard --with background_tasks,file_storage --yes`.
For an interactive run with prompts, drop the `--yes`. For a preset + extras: `smpy new myapp --preset standard --with background_tasks,file_storage --yes`.

## 3. Boot it

Expand All @@ -39,15 +39,15 @@ The API and Vite dev servers start side by side. Visit:
In another terminal, from inside `myapp`:

```bash
uv run sm-users create-admin --email admin@example.com --password changeme
uv run smpy users create-admin --email admin@example.com --password changeme
```

Sign in at `/users/login` and you land on the dashboard.

## 5. Scaffold a new module

```bash
sm create-module orders --dest modules/orders
smpy create-module orders --dest modules/orders
uv add ./modules/orders
```

Expand Down
4 changes: 2 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,15 @@ features:

```bash
uv tool install simple_module_cli
sm new myapp --yes
smpy new myapp --yes
cd myapp
make dev # API on :8000, Vite on :5050
```

Then in another terminal, inside `myapp`:

```bash
sm create-module orders --dest modules/orders
smpy create-module orders --dest modules/orders
uv add ./modules/orders
```

Expand Down
4 changes: 2 additions & 2 deletions docs/module-authoring.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Module authoring guide

This is the reference for authoring a module that is installable from PyPI
and assembled into a host by the `sm create-host` scaffold. It describes the
and assembled into a host by the `smpy create-host` scaffold. It describes the
contract a module must follow, the env-var conventions, the migration
workflow the host developer uses, and the API-version / semver rules.

Expand Down Expand Up @@ -256,7 +256,7 @@ templates by copying + editing.

## Testing during development

Install `simple_module_test` as a dev dependency (the `sm create-module`
Install `simple_module_test` as a dev dependency (the `smpy create-module`
scaffold does this automatically):

```toml
Expand Down
2 changes: 1 addition & 1 deletion docs/modules/dashboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,4 @@ Top-level keys in `dashboard/locales/en.json`:

## Replacing it

If you want a different post-login landing page, set `users.login_redirect_url` in the [admin settings UI](/modules/settings) (or via `sm-settings import-from-env` from `SM_USERS_LOGIN_REDIRECT_URL`) to your route. You can keep the dashboard module installed for the menu entry, or set `SM_MODULES_ENABLED` without `dashboard` to drop it entirely. The `users` module auto-detects whether `dashboard` is installed and falls back to `/` if not.
If you want a different post-login landing page, set `users.login_redirect_url` in the [admin settings UI](/modules/settings) (or via `smpy settings import-from-env` from `SM_USERS_LOGIN_REDIRECT_URL`) to your route. You can keep the dashboard module installed for the menu entry, or set `SM_MODULES_ENABLED` without `dashboard` to drop it entirely. The `users` module auto-detects whether `dashboard` is installed and falls back to `/` if not.
Loading
Loading