cleanarch 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cleanarch-0.1.0/.github/workflows/ci.yml +42 -0
- cleanarch-0.1.0/.github/workflows/publish.yml +75 -0
- cleanarch-0.1.0/.gitignore +133 -0
- cleanarch-0.1.0/PKG-INFO +106 -0
- cleanarch-0.1.0/README.md +93 -0
- cleanarch-0.1.0/Taskfile.yml +34 -0
- cleanarch-0.1.0/mise.toml +4 -0
- cleanarch-0.1.0/pyproject.toml +108 -0
- cleanarch-0.1.0/src/cleanarchitecture/__init__.py +11 -0
- cleanarch-0.1.0/src/cleanarchitecture/__main__.py +5 -0
- cleanarch-0.1.0/src/cleanarchitecture/_lint_runner.py +24 -0
- cleanarch-0.1.0/src/cleanarchitecture/cli.py +91 -0
- cleanarch-0.1.0/src/cleanarchitecture/config.py +142 -0
- cleanarch-0.1.0/src/cleanarchitecture/contracts.py +207 -0
- cleanarch-0.1.0/src/cleanarchitecture/overrides.py +64 -0
- cleanarch-0.1.0/test/__init__.py +0 -0
- cleanarch-0.1.0/test/test_/344/270/212/346/233/270/343/201/215/345/256/243/350/250/200.py +102 -0
- cleanarch-0.1.0/test/test_/345/245/221/347/264/204/347/224/237/346/210/220.py +184 -0
- cleanarch-0.1.0/uv.lock +406 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: Python CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main ]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
permissions: {}
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
validation:
|
|
14
|
+
name: 検査
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
timeout-minutes: 10
|
|
17
|
+
permissions:
|
|
18
|
+
contents: read
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
21
|
+
with:
|
|
22
|
+
persist-credentials: false
|
|
23
|
+
|
|
24
|
+
- name: uv のセットアップ
|
|
25
|
+
# setup-uv は major/minor タグを publish していないため commit hash でピンする
|
|
26
|
+
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
27
|
+
with:
|
|
28
|
+
python-version: "3.14"
|
|
29
|
+
enable-cache: true
|
|
30
|
+
|
|
31
|
+
- name: 依存の同期
|
|
32
|
+
# --locked で uv.lock と pyproject.toml のズレも検出する
|
|
33
|
+
run: uv sync --locked
|
|
34
|
+
|
|
35
|
+
- name: Ruff リント
|
|
36
|
+
run: uv run ruff check .
|
|
37
|
+
|
|
38
|
+
- name: 型チェック
|
|
39
|
+
run: uv run mypy
|
|
40
|
+
|
|
41
|
+
- name: テスト
|
|
42
|
+
run: uv run pytest
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
# 既定は無権限。必要な job にだけ最小権限を与える。
|
|
8
|
+
permissions: {}
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
group: publish-${{ github.ref }}
|
|
12
|
+
cancel-in-progress: false
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
build:
|
|
16
|
+
name: 配布物のビルド
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
timeout-minutes: 10
|
|
19
|
+
permissions:
|
|
20
|
+
contents: read
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
23
|
+
with:
|
|
24
|
+
persist-credentials: false
|
|
25
|
+
|
|
26
|
+
- name: uv のセットアップ
|
|
27
|
+
# setup-uv は major/minor タグを publish していないため commit hash でピンする
|
|
28
|
+
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
29
|
+
with:
|
|
30
|
+
python-version: "3.14"
|
|
31
|
+
# リリース成果物のビルドではキャッシュを使わない。
|
|
32
|
+
# PR 経由で汚染されたキャッシュが配布物に混入する経路を断つ (zizmor: cache-poisoning)。
|
|
33
|
+
enable-cache: false
|
|
34
|
+
|
|
35
|
+
- name: タグと pyproject.toml の version が一致することを検証
|
|
36
|
+
env:
|
|
37
|
+
TAG: ${{ github.event.release.tag_name }}
|
|
38
|
+
run: |
|
|
39
|
+
VERSION="$(python3 -c 'import tomllib, pathlib; print(tomllib.loads(pathlib.Path("pyproject.toml").read_text())["project"]["version"])')"
|
|
40
|
+
if [ "${TAG#v}" != "$VERSION" ]; then
|
|
41
|
+
echo "::error::タグ ${TAG} と pyproject.toml の version ${VERSION} が一致しません"
|
|
42
|
+
exit 1
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
- name: sdist と wheel をビルド
|
|
46
|
+
run: uv build --out-dir dist
|
|
47
|
+
|
|
48
|
+
- name: 配布物を artifact として保存
|
|
49
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
50
|
+
with:
|
|
51
|
+
name: python-package-distributions
|
|
52
|
+
path: dist/
|
|
53
|
+
|
|
54
|
+
publish-to-pypi:
|
|
55
|
+
name: PyPI へ公開
|
|
56
|
+
needs: [ build ]
|
|
57
|
+
runs-on: ubuntu-latest
|
|
58
|
+
timeout-minutes: 10
|
|
59
|
+
environment:
|
|
60
|
+
name: pypi
|
|
61
|
+
url: https://pypi.org/p/cleanarch
|
|
62
|
+
permissions:
|
|
63
|
+
# OIDC トークンの発行に必須。workflow 単位ではなく job 単位で付与する (公式の強い推奨)
|
|
64
|
+
id-token: write
|
|
65
|
+
steps:
|
|
66
|
+
- name: 配布物を取得
|
|
67
|
+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
68
|
+
with:
|
|
69
|
+
name: python-package-distributions
|
|
70
|
+
path: dist/
|
|
71
|
+
|
|
72
|
+
- name: PyPI へアップロード
|
|
73
|
+
# Trusted Publishing (OIDC) のため認証情報の指定は一切不要。
|
|
74
|
+
# v1.11.0 以降、Trusted Publishing 時は PEP 740 attestation が自動生成・添付される。
|
|
75
|
+
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
pip-wheel-metadata/
|
|
24
|
+
share/python-wheels/
|
|
25
|
+
*.egg-info/
|
|
26
|
+
.installed.cfg
|
|
27
|
+
*.egg
|
|
28
|
+
MANIFEST
|
|
29
|
+
|
|
30
|
+
# PyInstaller
|
|
31
|
+
# Usually these files are written by a python script from a template
|
|
32
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
33
|
+
*.manifest
|
|
34
|
+
*.spec
|
|
35
|
+
|
|
36
|
+
# Installer logs
|
|
37
|
+
pip-log.txt
|
|
38
|
+
pip-delete-this-directory.txt
|
|
39
|
+
|
|
40
|
+
# Unit test / coverage reports
|
|
41
|
+
htmlcov/
|
|
42
|
+
.tox/
|
|
43
|
+
.nox/
|
|
44
|
+
.coverage
|
|
45
|
+
.coverage.*
|
|
46
|
+
.cache
|
|
47
|
+
nosetests.xml
|
|
48
|
+
coverage.xml
|
|
49
|
+
*.cover
|
|
50
|
+
*.py,cover
|
|
51
|
+
.hypothesis/
|
|
52
|
+
.pytest_cache/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
target/
|
|
76
|
+
|
|
77
|
+
# Jupyter Notebook
|
|
78
|
+
.ipynb_checkpoints
|
|
79
|
+
|
|
80
|
+
# IPython
|
|
81
|
+
profile_default/
|
|
82
|
+
ipython_config.py
|
|
83
|
+
|
|
84
|
+
# pyenv
|
|
85
|
+
.python-version
|
|
86
|
+
|
|
87
|
+
# pipenv
|
|
88
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
89
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
90
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
91
|
+
# install all needed dependencies.
|
|
92
|
+
#Pipfile.lock
|
|
93
|
+
|
|
94
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
|
95
|
+
__pypackages__/
|
|
96
|
+
|
|
97
|
+
# Celery stuff
|
|
98
|
+
celerybeat-schedule
|
|
99
|
+
celerybeat.pid
|
|
100
|
+
|
|
101
|
+
# SageMath parsed files
|
|
102
|
+
*.sage.py
|
|
103
|
+
|
|
104
|
+
# Environments
|
|
105
|
+
.env
|
|
106
|
+
.venv
|
|
107
|
+
env/
|
|
108
|
+
venv/
|
|
109
|
+
ENV/
|
|
110
|
+
env.bak/
|
|
111
|
+
venv.bak/
|
|
112
|
+
|
|
113
|
+
# Spyder project settings
|
|
114
|
+
.spyderproject
|
|
115
|
+
.spyproject
|
|
116
|
+
|
|
117
|
+
# Rope project settings
|
|
118
|
+
.ropeproject
|
|
119
|
+
|
|
120
|
+
# mkdocs documentation
|
|
121
|
+
/site
|
|
122
|
+
|
|
123
|
+
# mypy
|
|
124
|
+
.mypy_cache/
|
|
125
|
+
.dmypy.json
|
|
126
|
+
dmypy.json
|
|
127
|
+
|
|
128
|
+
# Pyre type checker
|
|
129
|
+
.pyre/
|
|
130
|
+
|
|
131
|
+
# JetBrains IDE (IntelliJ IDEA / PyCharm)
|
|
132
|
+
# 各開発者のローカル設定なので git 管理しない
|
|
133
|
+
.idea/
|
cleanarch-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: cleanarch
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: モジュラモノリス + DDD のアーキテクチャ契約を「設定ファイルではなく CLI」として配る検査ツール
|
|
5
|
+
Project-URL: Homepage, https://github.com/theindiehacker/clean-architecture
|
|
6
|
+
Project-URL: Repository, https://github.com/theindiehacker/clean-architecture
|
|
7
|
+
Author-email: taiyo tamura <gtaiyou24@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Keywords: architecture,ddd,import-linter,lint,modular-monolith
|
|
10
|
+
Requires-Python: >=3.14
|
|
11
|
+
Requires-Dist: import-linter==2.13
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# 🏗️ Clean Architecture
|
|
15
|
+
|
|
16
|
+
モジュラモノリス + DDD のアーキテクチャ契約を、**設定ファイルではなく CLI として配る**検査ツール。
|
|
17
|
+
|
|
18
|
+
## 何が違うか
|
|
19
|
+
|
|
20
|
+
通常 [import-linter](https://import-linter.readthedocs.io/) の契約は各リポジトリの `.importlinter` に書く。
|
|
21
|
+
モジュールが増えれば契約ファイルも増え、プロジェクトが増えれば同じ文面がコピーで増殖する。
|
|
22
|
+
そして中央で契約を 1 本足しても、どのリポジトリにも届かない。
|
|
23
|
+
|
|
24
|
+
`cleanarch` は契約を**このパッケージの中**に持つ。利用側が書くのは宣言 1 ブロックだけで、
|
|
25
|
+
生成された契約はテンポラリファイルに書かれてそのまま捨てられる(リポジトリにコミットさせない
|
|
26
|
+
= 手で編集される余地を残さない)。契約を足したいときはこのパッケージのバージョンを上げる。
|
|
27
|
+
|
|
28
|
+
## 生成される契約
|
|
29
|
+
|
|
30
|
+
| 契約 | 内容 |
|
|
31
|
+
|:--|:--|
|
|
32
|
+
| `{module}-layers` | ヘキサゴナルの依存方向(`port → application → domain`)を exhaustive で強制 |
|
|
33
|
+
| `{module}-inbound-adapters` | 入力アダプタから domain への直接依存を禁止(ユースケース境界の空洞化を防ぐ) |
|
|
34
|
+
| `{module}-encapsulation` | 他モジュールから内部層への参照を禁止。**source は「自分以外の全モジュール」から自動生成** |
|
|
35
|
+
| `{shared}-purity` | 共有カーネルから業務モジュールへの依存を禁止(逆流防止) |
|
|
36
|
+
|
|
37
|
+
`{module}-encapsulation` の source を自動生成しているのが効く。手書きの契約ファイルでは、
|
|
38
|
+
モジュールを 1 つ足したときに既存モジュール全部の `source_modules` へ追記する必要があり、
|
|
39
|
+
**漏れがそのまま境界の穴になる**(fastship.jp の `.importlinter` にもこの注意書きがある)。
|
|
40
|
+
生成ならこの事故が起こらない。
|
|
41
|
+
|
|
42
|
+
## 導入
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
uv add --dev cleanarch
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## 設定
|
|
49
|
+
|
|
50
|
+
```toml
|
|
51
|
+
[tool.cleanarch]
|
|
52
|
+
src = "src"
|
|
53
|
+
modules = ["authority", "tenant", "notify"] # 省略時は src/* を自動検出
|
|
54
|
+
shared = ["common"]
|
|
55
|
+
|
|
56
|
+
# 既定値を上書きしたいとき
|
|
57
|
+
layers = ["port", "application", "domain"]
|
|
58
|
+
layer_ignores = ["core", "middleware", "exception", "settings"]
|
|
59
|
+
internal_layers = ["application", "domain", "port.adapter.persistence", "port.adapter.service"]
|
|
60
|
+
inbound_adapters = ["port.adapter.resource", "port.adapter.messaging"]
|
|
61
|
+
|
|
62
|
+
# 既存違反は負債として明示する。新規違反だけが CI を落とす。
|
|
63
|
+
debt = ["authority.port.adapter.resource.oauth.scopes_resource -> authority.domain.model.scope"]
|
|
64
|
+
|
|
65
|
+
# プロジェクト固有の契約
|
|
66
|
+
[[tool.cleanarch.forbidden]]
|
|
67
|
+
name = "redis-direct-access"
|
|
68
|
+
description = "RedisRegistry 以外からの redis 直接 import を禁止"
|
|
69
|
+
source_modules = ["authority", "tenant"]
|
|
70
|
+
forbidden_modules = ["redis"]
|
|
71
|
+
|
|
72
|
+
# フレームワーク実装の上書き(期限付きの負債)
|
|
73
|
+
[[tool.cleanarch.overrides]]
|
|
74
|
+
target = "authority.application.identity.IdentityApplicationService"
|
|
75
|
+
reason = "Identity Platform への資格情報移管。CredentialService ポートが未提供"
|
|
76
|
+
upstream = "https://github.com/theindiehacker/clean-architecture/issues/128"
|
|
77
|
+
sunset = 2026-12-31
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## コマンド
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
cleanarch check # 契約 + 上書き宣言を検査する(CI で使う。違反 or 期限切れで非 0 終了)
|
|
84
|
+
cleanarch contracts # 生成される import-linter 契約を表示する(デバッグ用)
|
|
85
|
+
cleanarch overrides # 上書き宣言の一覧と期限を表示する
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## 負債と上書きを 1 箇所に集める
|
|
89
|
+
|
|
90
|
+
`debt` と `overrides` をプロジェクト全体で 1 箇所に集約しているのは、**総量を中央から観測する**ため。
|
|
91
|
+
契約ファイルが 9 個に散っていると数えられない。
|
|
92
|
+
|
|
93
|
+
とくに `overrides` の一覧は、そのまま「フレームワークに足りない拡張点のバックログ」になる。
|
|
94
|
+
同じ上書きが 2 案件で現れたら、拡張点の不足が確定した合図。
|
|
95
|
+
|
|
96
|
+
## 開発
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
task init # 依存インストール
|
|
100
|
+
task test # テスト
|
|
101
|
+
task style:check # ruff / mypy
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## ライセンス
|
|
105
|
+
|
|
106
|
+
MIT
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# 🏗️ Clean Architecture
|
|
2
|
+
|
|
3
|
+
モジュラモノリス + DDD のアーキテクチャ契約を、**設定ファイルではなく CLI として配る**検査ツール。
|
|
4
|
+
|
|
5
|
+
## 何が違うか
|
|
6
|
+
|
|
7
|
+
通常 [import-linter](https://import-linter.readthedocs.io/) の契約は各リポジトリの `.importlinter` に書く。
|
|
8
|
+
モジュールが増えれば契約ファイルも増え、プロジェクトが増えれば同じ文面がコピーで増殖する。
|
|
9
|
+
そして中央で契約を 1 本足しても、どのリポジトリにも届かない。
|
|
10
|
+
|
|
11
|
+
`cleanarch` は契約を**このパッケージの中**に持つ。利用側が書くのは宣言 1 ブロックだけで、
|
|
12
|
+
生成された契約はテンポラリファイルに書かれてそのまま捨てられる(リポジトリにコミットさせない
|
|
13
|
+
= 手で編集される余地を残さない)。契約を足したいときはこのパッケージのバージョンを上げる。
|
|
14
|
+
|
|
15
|
+
## 生成される契約
|
|
16
|
+
|
|
17
|
+
| 契約 | 内容 |
|
|
18
|
+
|:--|:--|
|
|
19
|
+
| `{module}-layers` | ヘキサゴナルの依存方向(`port → application → domain`)を exhaustive で強制 |
|
|
20
|
+
| `{module}-inbound-adapters` | 入力アダプタから domain への直接依存を禁止(ユースケース境界の空洞化を防ぐ) |
|
|
21
|
+
| `{module}-encapsulation` | 他モジュールから内部層への参照を禁止。**source は「自分以外の全モジュール」から自動生成** |
|
|
22
|
+
| `{shared}-purity` | 共有カーネルから業務モジュールへの依存を禁止(逆流防止) |
|
|
23
|
+
|
|
24
|
+
`{module}-encapsulation` の source を自動生成しているのが効く。手書きの契約ファイルでは、
|
|
25
|
+
モジュールを 1 つ足したときに既存モジュール全部の `source_modules` へ追記する必要があり、
|
|
26
|
+
**漏れがそのまま境界の穴になる**(fastship.jp の `.importlinter` にもこの注意書きがある)。
|
|
27
|
+
生成ならこの事故が起こらない。
|
|
28
|
+
|
|
29
|
+
## 導入
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
uv add --dev cleanarch
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## 設定
|
|
36
|
+
|
|
37
|
+
```toml
|
|
38
|
+
[tool.cleanarch]
|
|
39
|
+
src = "src"
|
|
40
|
+
modules = ["authority", "tenant", "notify"] # 省略時は src/* を自動検出
|
|
41
|
+
shared = ["common"]
|
|
42
|
+
|
|
43
|
+
# 既定値を上書きしたいとき
|
|
44
|
+
layers = ["port", "application", "domain"]
|
|
45
|
+
layer_ignores = ["core", "middleware", "exception", "settings"]
|
|
46
|
+
internal_layers = ["application", "domain", "port.adapter.persistence", "port.adapter.service"]
|
|
47
|
+
inbound_adapters = ["port.adapter.resource", "port.adapter.messaging"]
|
|
48
|
+
|
|
49
|
+
# 既存違反は負債として明示する。新規違反だけが CI を落とす。
|
|
50
|
+
debt = ["authority.port.adapter.resource.oauth.scopes_resource -> authority.domain.model.scope"]
|
|
51
|
+
|
|
52
|
+
# プロジェクト固有の契約
|
|
53
|
+
[[tool.cleanarch.forbidden]]
|
|
54
|
+
name = "redis-direct-access"
|
|
55
|
+
description = "RedisRegistry 以外からの redis 直接 import を禁止"
|
|
56
|
+
source_modules = ["authority", "tenant"]
|
|
57
|
+
forbidden_modules = ["redis"]
|
|
58
|
+
|
|
59
|
+
# フレームワーク実装の上書き(期限付きの負債)
|
|
60
|
+
[[tool.cleanarch.overrides]]
|
|
61
|
+
target = "authority.application.identity.IdentityApplicationService"
|
|
62
|
+
reason = "Identity Platform への資格情報移管。CredentialService ポートが未提供"
|
|
63
|
+
upstream = "https://github.com/theindiehacker/clean-architecture/issues/128"
|
|
64
|
+
sunset = 2026-12-31
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## コマンド
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
cleanarch check # 契約 + 上書き宣言を検査する(CI で使う。違反 or 期限切れで非 0 終了)
|
|
71
|
+
cleanarch contracts # 生成される import-linter 契約を表示する(デバッグ用)
|
|
72
|
+
cleanarch overrides # 上書き宣言の一覧と期限を表示する
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## 負債と上書きを 1 箇所に集める
|
|
76
|
+
|
|
77
|
+
`debt` と `overrides` をプロジェクト全体で 1 箇所に集約しているのは、**総量を中央から観測する**ため。
|
|
78
|
+
契約ファイルが 9 個に散っていると数えられない。
|
|
79
|
+
|
|
80
|
+
とくに `overrides` の一覧は、そのまま「フレームワークに足りない拡張点のバックログ」になる。
|
|
81
|
+
同じ上書きが 2 案件で現れたら、拡張点の不足が確定した合図。
|
|
82
|
+
|
|
83
|
+
## 開発
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
task init # 依存インストール
|
|
87
|
+
task test # テスト
|
|
88
|
+
task style:check # ruff / mypy
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## ライセンス
|
|
92
|
+
|
|
93
|
+
MIT
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
version: '3'
|
|
2
|
+
|
|
3
|
+
tasks:
|
|
4
|
+
init:
|
|
5
|
+
desc: 開発環境のセットアップ
|
|
6
|
+
cmds:
|
|
7
|
+
- uv sync
|
|
8
|
+
- echo "✅ done"
|
|
9
|
+
|
|
10
|
+
test:
|
|
11
|
+
desc: テスト実行
|
|
12
|
+
cmd: uv run pytest {{.CLI_ARGS}}
|
|
13
|
+
|
|
14
|
+
style:check:
|
|
15
|
+
desc: スタイル / 型の検査
|
|
16
|
+
deps: [style:check:ruff, style:check:mypy]
|
|
17
|
+
cmds:
|
|
18
|
+
- echo "✅ clean-architecture"
|
|
19
|
+
|
|
20
|
+
style:check:ruff:
|
|
21
|
+
desc: Ruff リント
|
|
22
|
+
cmd: uv run ruff check .
|
|
23
|
+
|
|
24
|
+
style:check:mypy:
|
|
25
|
+
desc: 型チェック
|
|
26
|
+
cmd: uv run mypy
|
|
27
|
+
|
|
28
|
+
style:fix:
|
|
29
|
+
desc: スタイル自動修正
|
|
30
|
+
cmd: uv run ruff check --fix .
|
|
31
|
+
|
|
32
|
+
build:
|
|
33
|
+
desc: 配布物のビルド
|
|
34
|
+
cmd: uv build --out-dir dist
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "cleanarch"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "モジュラモノリス + DDD のアーキテクチャ契約を「設定ファイルではなく CLI」として配る検査ツール"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.14"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
authors = [{ name = "taiyo tamura", email = "gtaiyou24@gmail.com" }]
|
|
9
|
+
keywords = ["architecture", "import-linter", "modular-monolith", "ddd", "lint"]
|
|
10
|
+
dependencies = [
|
|
11
|
+
"import-linter==2.13",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[project.scripts]
|
|
15
|
+
cleanarch = "cleanarchitecture.cli:main"
|
|
16
|
+
|
|
17
|
+
[project.urls]
|
|
18
|
+
Homepage = "https://github.com/theindiehacker/clean-architecture"
|
|
19
|
+
Repository = "https://github.com/theindiehacker/clean-architecture"
|
|
20
|
+
|
|
21
|
+
[build-system]
|
|
22
|
+
requires = ["hatchling"]
|
|
23
|
+
build-backend = "hatchling.build"
|
|
24
|
+
|
|
25
|
+
[tool.hatch.build.targets.wheel]
|
|
26
|
+
packages = ["src/cleanarchitecture"]
|
|
27
|
+
|
|
28
|
+
[dependency-groups]
|
|
29
|
+
dev = [
|
|
30
|
+
"mypy==2.1.0",
|
|
31
|
+
"pytest==9.1.1",
|
|
32
|
+
"ruff==0.15.21",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
# ============ 🪄 Ruff ============
|
|
36
|
+
# 利用側プロジェクトに配る規約と揃える (自分が守れない規約は配れない)。
|
|
37
|
+
[tool.ruff]
|
|
38
|
+
# src レイアウト。cleanarchitecture を第一パーティとして isort に認識させる。
|
|
39
|
+
src = ["src", "."]
|
|
40
|
+
target-version = "py314"
|
|
41
|
+
line-length = 120
|
|
42
|
+
indent-width = 4
|
|
43
|
+
extend-exclude = ["__init__.py"]
|
|
44
|
+
|
|
45
|
+
[tool.ruff.lint]
|
|
46
|
+
select = ["ALL"]
|
|
47
|
+
ignore = [
|
|
48
|
+
"B008",
|
|
49
|
+
"ERA001",
|
|
50
|
+
"FIX002",
|
|
51
|
+
"N818",
|
|
52
|
+
"PLR0913",
|
|
53
|
+
"RUF012",
|
|
54
|
+
"TRY003",
|
|
55
|
+
"FBT001",
|
|
56
|
+
"FBT002",
|
|
57
|
+
"PLC0415",
|
|
58
|
+
"PLW0108",
|
|
59
|
+
"A003",
|
|
60
|
+
"D",
|
|
61
|
+
"TD",
|
|
62
|
+
"ARG",
|
|
63
|
+
"EM",
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
[tool.ruff.lint.per-file-ignores]
|
|
67
|
+
# CLI は標準出力が成果物。
|
|
68
|
+
"src/cleanarchitecture/cli.py" = ["T201"]
|
|
69
|
+
"src/cleanarchitecture/_lint_runner.py" = ["T201"]
|
|
70
|
+
"src/cleanarchitecture/overrides.py" = ["T201"]
|
|
71
|
+
# テストは日本語で仕様を書く (テスト名がそのまま仕様書になる)。
|
|
72
|
+
"test/**/*.py" = ["S101", "SLF001", "PLR2004", "INP001", "N801", "N802", "N803", "N806", "N999", "PLC2401", "RUF001", "RUF002", "RUF003",
|
|
73
|
+
# テストは fixture / tmp_path を実行時に受け取るため型限定 import へ移せない
|
|
74
|
+
"TC001", "TC002", "TC003",
|
|
75
|
+
# 期限判定は「今日」が基準。テストでも同じ基準を使う
|
|
76
|
+
"DTZ011"]
|
|
77
|
+
|
|
78
|
+
[tool.ruff.lint.pycodestyle]
|
|
79
|
+
max-line-length = 160
|
|
80
|
+
|
|
81
|
+
[tool.ruff.lint.flake8-annotations]
|
|
82
|
+
mypy-init-return = true
|
|
83
|
+
|
|
84
|
+
[tool.ruff.lint.flake8-tidy-imports]
|
|
85
|
+
ban-relative-imports = "all"
|
|
86
|
+
|
|
87
|
+
[tool.ruff.lint.isort.sections]
|
|
88
|
+
sections = ["FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"]
|
|
89
|
+
|
|
90
|
+
# ============ ✔️ mypy ============
|
|
91
|
+
[tool.mypy]
|
|
92
|
+
python_version = 3.14
|
|
93
|
+
files = ["src", "test"]
|
|
94
|
+
mypy_path = ["src"]
|
|
95
|
+
color_output = true
|
|
96
|
+
show_column_numbers = true
|
|
97
|
+
check_untyped_defs = true
|
|
98
|
+
disallow_untyped_defs = true
|
|
99
|
+
no_implicit_optional = true
|
|
100
|
+
warn_redundant_casts = true
|
|
101
|
+
ignore_missing_imports = true
|
|
102
|
+
strict_optional = false
|
|
103
|
+
explicit_package_bases = true
|
|
104
|
+
|
|
105
|
+
# ============ 🧪 pytest ============
|
|
106
|
+
[tool.pytest.ini_options]
|
|
107
|
+
testpaths = ["test"]
|
|
108
|
+
pythonpath = ["src"]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""import-linter を子プロセスとして実行するエントリポイント。
|
|
2
|
+
|
|
3
|
+
`python -m cleanarchitecture._lint_runner <生成した .ini>` として `cli` から起動される。
|
|
4
|
+
別プロセスに分けるのは、import-linter が検査対象プロジェクトのソースを実際に import するため。
|
|
5
|
+
CLI 自身のプロセスに読み込ませると、利用側の import 副作用と sys.path 汚染を持ち込んでしまう。
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
from importlinter.application.use_cases import lint_imports
|
|
11
|
+
from importlinter.configuration import configure
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def main(argv: list[str] | None = None) -> int:
|
|
15
|
+
args = sys.argv[1:] if argv is None else argv
|
|
16
|
+
if len(args) != 1:
|
|
17
|
+
print("usage: python -m cleanarchitecture._lint_runner <config.ini>", file=sys.stderr)
|
|
18
|
+
return 2
|
|
19
|
+
configure()
|
|
20
|
+
return 0 if lint_imports(config_filename=args[0], cache_dir=None) else 1
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
if __name__ == "__main__":
|
|
24
|
+
sys.exit(main())
|