Skip to content

Commit b835663

Browse files
authored
Reduce cognitive complexity (#22)
* feat: implement django-dbml core with schema generation, rendering, and CLI command refactor to modular architecture * ci: enhance GitHub Actions workflows with gated release flow, metadata checks, and artifact reuse
1 parent 28fc467 commit b835663

File tree

17 files changed

+1053
-551
lines changed

17 files changed

+1053
-551
lines changed

.github/workflows/ci.yml

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
name: CI
22

33
on:
4+
workflow_call:
45
pull_request:
56
push:
67

@@ -53,8 +54,9 @@ jobs:
5354
- uses: astral-sh/setup-uv@v5
5455
- name: Run tests
5556
run: make test-django PYTHON=${{ matrix.python-version }} DJANGO_CONSTRAINT='${{ matrix.django-constraint }}'
56-
quality:
57-
name: Quality
57+
58+
lint:
59+
name: Lint
5860
runs-on: ubuntu-latest
5961
steps:
6062
- uses: actions/checkout@v4
@@ -66,5 +68,15 @@ jobs:
6668
run: make sync
6769
- name: Run lint
6870
run: make lint
71+
72+
package-check:
73+
name: Package Check
74+
runs-on: ubuntu-latest
75+
steps:
76+
- uses: actions/checkout@v4
77+
- uses: actions/setup-python@v5
78+
with:
79+
python-version: "3.13"
80+
- uses: astral-sh/setup-uv@v5
6981
- name: Build package
7082
run: make build

.github/workflows/production.yml

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,45 @@ name: Publish 📦 to PyPI
22

33
on:
44
push:
5-
branches: [main]
5+
tags:
6+
- "v*"
67

78
jobs:
8-
release:
9+
metadata:
10+
name: Release Metadata
911
runs-on: ubuntu-latest
12+
outputs:
13+
package_version: ${{ steps.version.outputs.package_version }}
14+
git_tag: ${{ steps.version.outputs.git_tag }}
15+
steps:
16+
- uses: actions/checkout@v4
17+
- name: Read package version
18+
id: version
19+
run: |
20+
python3 - <<'PY'
21+
import os
22+
import tomllib
23+
from pathlib import Path
24+
25+
git_tag = os.environ["GITHUB_REF_NAME"]
26+
version = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))["project"]["version"]
27+
expected_tag = f"v{version}"
28+
if git_tag != expected_tag:
29+
raise SystemExit(f"Tag {git_tag!r} does not match project version {expected_tag!r}")
30+
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as fh:
31+
fh.write(f"package_version={version}\n")
32+
fh.write(f"git_tag={git_tag}\n")
33+
print(f"{git_tag} -> {version}")
34+
PY
35+
36+
verify:
37+
name: Verify Release Candidate
38+
uses: ./.github/workflows/ci.yml
39+
40+
build:
41+
name: Build Release (${{ needs.metadata.outputs.package_version }})
42+
runs-on: ubuntu-latest
43+
needs: [metadata, verify]
1044
permissions:
1145
contents: read
1246
steps:
@@ -15,9 +49,32 @@ jobs:
1549
with:
1650
python-version: "3.13"
1751
- uses: astral-sh/setup-uv@v5
52+
- name: Sync dependencies
53+
run: make sync
1854
- name: Build package
19-
run: uv build
55+
run: make build
56+
- name: Upload distribution artifacts
57+
uses: actions/upload-artifact@v4
58+
with:
59+
name: dist-${{ needs.metadata.outputs.package_version }}
60+
path: dist/*
61+
if-no-files-found: error
62+
63+
publish:
64+
name: Publish Release (${{ needs.metadata.outputs.package_version }})
65+
runs-on: ubuntu-latest
66+
needs: [metadata, build]
67+
permissions:
68+
contents: read
69+
id-token: write
70+
environment: pypi
71+
steps:
72+
- name: Download distribution artifacts
73+
uses: actions/download-artifact@v4
74+
with:
75+
name: dist-${{ needs.metadata.outputs.package_version }}
76+
path: dist
2077
- name: Publish package
21-
env:
22-
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_TOKEN }}
23-
run: uv publish --trusted-publishing never
78+
uses: pypa/gh-action-pypi-publish@release/v1
79+
with:
80+
packages-dir: dist
Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,38 @@
11
name: Publish 📦 to PyPI test server 🐍
22

3-
on: push
4-
3+
on:
4+
workflow_dispatch:
55

66
jobs:
7-
release:
7+
metadata:
8+
name: Release Metadata
9+
runs-on: ubuntu-latest
10+
outputs:
11+
package_version: ${{ steps.version.outputs.package_version }}
12+
steps:
13+
- uses: actions/checkout@v4
14+
- name: Read package version
15+
id: version
16+
run: |
17+
python3 - <<'PY'
18+
import os
19+
import tomllib
20+
from pathlib import Path
21+
22+
version = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))["project"]["version"]
23+
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as fh:
24+
fh.write(f"package_version={version}\n")
25+
print(version)
26+
PY
27+
28+
verify:
29+
name: Verify TestPyPI Candidate
30+
uses: ./.github/workflows/ci.yml
31+
32+
build:
33+
name: Build TestPyPI Release (${{ needs.metadata.outputs.package_version }})
834
runs-on: ubuntu-latest
35+
needs: [metadata, verify]
936
permissions:
1037
contents: read
1138
steps:
@@ -14,9 +41,33 @@ jobs:
1441
with:
1542
python-version: "3.13"
1643
- uses: astral-sh/setup-uv@v5
44+
- name: Sync dependencies
45+
run: make sync
1746
- name: Build package
18-
run: uv build
47+
run: make build
48+
- name: Upload distribution artifacts
49+
uses: actions/upload-artifact@v4
50+
with:
51+
name: dist-${{ needs.metadata.outputs.package_version }}
52+
path: dist/*
53+
if-no-files-found: error
54+
55+
publish:
56+
name: Publish TestPyPI Release (${{ needs.metadata.outputs.package_version }})
57+
runs-on: ubuntu-latest
58+
needs: [metadata, build]
59+
permissions:
60+
contents: read
61+
id-token: write
62+
environment: testpypi
63+
steps:
64+
- name: Download distribution artifacts
65+
uses: actions/download-artifact@v4
66+
with:
67+
name: dist-${{ needs.metadata.outputs.package_version }}
68+
path: dist
1969
- name: Publish package
20-
env:
21-
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_TEST_TOKEN }}
22-
run: uv publish --publish-url https://test.pypi.org/legacy/ --check-url https://test.pypi.org/simple/ --trusted-publishing never
70+
uses: pypa/gh-action-pypi-publish@release/v1
71+
with:
72+
packages-dir: dist
73+
repository-url: https://test.pypi.org/legacy/

CONTRIBUTING.md

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,20 +48,52 @@ make test-django DJANGO_CONSTRAINT="django>=5.1,<5.2" PYTHON=3.13
4848

4949
## Project layout
5050

51-
- `django_dbml/management/commands/dbml.py`: command that inspects Django model metadata and renders DBML
52-
- `django_dbml/utils.py`: helper utilities used by the generator
51+
- `django_dbml/management/commands/dbml.py`: thin Django management command entrypoint
52+
- `django_dbml/core/options.py`: generation options shared across the core
53+
- `django_dbml/core/selection.py`: model selection and related-model expansion
54+
- `django_dbml/core/builder.py`: Django model introspection and schema assembly
55+
- `django_dbml/core/renderer.py`: DBML rendering
56+
- `django_dbml/core/schema.py`: intermediate dataclasses for tables, fields, indexes, enums, and relations
57+
- `django_dbml/utils.py`: small string-formatting helpers
5358
- `tests/testapp/`: isolated Django app used to exercise the extension
5459
- `tests/test_command.py`: command-level tests
5560
- `tests/test_utils.py`: unit tests for helper behavior
5661

5762
## Release flow
5863

59-
The repository publishes from GitHub Actions using `uv build` and `uv publish`. Before releasing, run:
64+
The repository publishes from GitHub Actions using a gated release flow:
65+
66+
- `CI` runs the Django/Python compatibility matrix
67+
- `CI` runs lint separately
68+
- `CI` runs a package build check separately
69+
- publish workflows reuse `CI` before building release artifacts
70+
- release artifacts are built once, uploaded, and published from those exact artifacts
71+
- production publishing happens only from tags in the format `vX.Y.Z`
72+
- the production workflow validates that the Git tag matches `project.version`
73+
- publishing uses PyPI Trusted Publishing, not long-lived API tokens
74+
- TestPyPI publishing is manual via `workflow_dispatch`
75+
- TestPyPI can also use Trusted Publishing when configured on TestPyPI
76+
77+
Before releasing locally, run:
6078

6179
```bash
6280
make test
6381
make lint
6482
make build
6583
```
6684

85+
Recommended production release flow:
86+
87+
```bash
88+
make test
89+
make lint
90+
make build
91+
git tag v1.0.1
92+
git push origin v1.0.1
93+
```
94+
95+
After the tag is pushed, the PyPI workflow publishes that version if CI passes and the tag matches the package version.
96+
97+
If you use GitHub Releases, create the release from the existing version tag instead of using branch pushes as the release trigger.
98+
6799
The detailed development guide lives in `docs/development.md`.

0 commit comments

Comments
 (0)