• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kimata / py-project / 20998698267

14 Jan 2026 02:59PM UTC coverage: 81.785% (-8.9%) from 90.703%
20998698267

push

github

kimata
feat: 依存関係更新機能を拡張

- プロジェクトの pyproject.toml の dependencies を更新する機能を追加
- config.yaml の extra_dev_deps を更新する機能を追加
- 差分表示オプション (-d) との連携を追加
- 新しい CLI オプション:
  - --include-projects: プロジェクトの dependencies も更新対象に
  - --include-config: config.yaml の extra_dev_deps も更新対象に
  - -p PROJECT: 特定プロジェクトのみを対象に

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

14 of 155 new or added lines in 2 files covered. (9.03%)

1 existing line in 1 file now uncovered.

1136 of 1389 relevant lines covered (81.79%)

0.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

35.77
/src/py_project/cli.py
1
#!/usr/bin/env python3
2
"""
3
複数の Python プロジェクトに標準的な設定を一括適用します。
4

5
Usage:
6
  py-project [-c CONFIG] [-a] [-p PROJECT]... [-t TYPE]... [-d] [-b] [-v] [options]
7
  py-project [-c CONFIG] --validate
8
  py-project [-c CONFIG] --list-projects
9
  py-project --list-configs
10
  py-project [-c CONFIG] --update-deps [-a] [-d] [--include-projects] [--include-config] [-p PROJECT]...
11

12
Options:
13
  -c CONFIG, --config CONFIG    CONFIG を設定ファイルとして読み込みます。[default: config.yaml]
14
  -a, --apply                   実際に変更を適用します。(指定しないとドライラン)
15
  -p PROJECT, --project PROJECT 対象プロジェクトを限定します。(複数指定可)
16
  -t TYPE, --type TYPE          対象設定タイプを限定します。(複数指定可)
17
  -d, --diff                    差分を詳細表示します。
18
  -b, --backup                  適用前にバックアップを作成します。
19
  -v, --verbose                 詳細ログを出力します。
20
  --no-sync                     pyproject.toml 更新後に uv sync を実行しません。
21
  --git-commit                  更新したファイルを git add & commit します。
22
  --git-push                    更新したファイルを git add & commit & push します。
23
  --validate                    設定ファイルの検証のみ行います。
24
  --list-projects               プロジェクト一覧を表示します。
25
  --list-configs                設定タイプ一覧を表示します。
26
  --update-deps                 依存関係を最新バージョンに更新します。
27
  --include-projects            プロジェクトの dependencies も更新対象にします。
28
  --include-config              config.yaml の extra_dev_deps も更新対象にします。
29
"""
30

31
from __future__ import annotations
1✔
32

33
import logging
1✔
34
import pathlib
1✔
35
import sys
1✔
36

37
import my_lib.config
1✔
38
import my_lib.cui_progress
1✔
39
import my_lib.logger
1✔
40
import rich.console
1✔
41
import rich.table
1✔
42

43
import py_project.applier
1✔
44
import py_project.config
1✔
45
import py_project.dep_updater
1✔
46
import py_project.handlers
1✔
47

48
_SCHEMA_CONFIG = "schema/config.schema"
1✔
49

50

51
def execute(
1✔
52
    config: py_project.config.Config,
53
    options: py_project.config.ApplyOptions,
54
    projects: list[str] | None = None,
55
    config_types: list[str] | None = None,
56
) -> int:
57
    """設定を適用する
58

59
    Args:
60
        config: アプリケーション設定
61
        options: 適用オプション
62
        projects: 対象プロジェクト名のリスト
63
        config_types: 対象設定タイプのリスト
64

65
    Returns:
66
        エラー数(0 なら成功)
67

68
    """
69
    console = rich.console.Console()
1✔
70
    progress = my_lib.cui_progress.ProgressManager(
1✔
71
        console=console,
72
        title=" 🐍 py-project ",
73
    )
74

75
    try:
1✔
76
        progress.start()
1✔
77

78
        summary = py_project.applier.apply_configs(
1✔
79
            config=config,
80
            options=options,
81
            projects=projects,
82
            config_types=config_types,
83
            console=console,
84
            progress=progress,
85
        )
86

87
        return summary.errors
1✔
88
    finally:
89
        progress.stop()
1✔
90

91

92
def show_config_types() -> None:
1✔
93
    """設定タイプ一覧を表示"""
94
    console = rich.console.Console()
1✔
95
    table = rich.table.Table(title="設定タイプ一覧")
1✔
96
    table.add_column("名前", style="cyan")
1✔
97
    table.add_column("説明")
1✔
98

99
    descriptions = {
1✔
100
        "pre-commit": "pre-commit 設定ファイル",
101
        "ruff": "ruff 設定ファイル",
102
        "yamllint": "yamllint 設定ファイル",
103
        "prettier": "prettier 設定ファイル",
104
        "python-version": ".python-version ファイル",
105
        "dockerignore": ".dockerignore ファイル",
106
        "gitignore": ".gitignore ファイル",
107
        "renovate": "renovate 設定ファイル",
108
        "pyproject": "pyproject.toml 共通セクション",
109
        "my-py-lib": "my-py-lib 依存関係の更新",
110
    }
111

112
    for name in py_project.handlers.HANDLERS:
1✔
113
        table.add_row(name, descriptions.get(name, ""))
1✔
114

115
    console.print(table)
1✔
116

117

118
def show_projects(config: py_project.config.Config) -> None:
1✔
119
    """プロジェクト一覧を表示"""
120
    console = rich.console.Console()
1✔
121
    table = rich.table.Table(title="プロジェクト一覧")
1✔
122
    table.add_column("名前", style="cyan")
1✔
123
    table.add_column("パス")
1✔
124
    table.add_column("設定タイプ")
1✔
125

126
    default_configs = config.defaults.configs
1✔
127

128
    for proj in config.projects:
1✔
129
        configs = proj.configs if proj.configs is not None else default_configs
1✔
130
        configs_str = ", ".join(configs) if configs else "(デフォルト)"
1✔
131

132
        table.add_row(proj.name, proj.path, configs_str)
1✔
133

134
    console.print(table)
1✔
135

136

137
def main() -> None:
1✔
138
    """CLI エントリポイント"""
139
    import docopt
×
140

141
    if __doc__ is None:
×
142
        raise RuntimeError("__doc__ is not set")
×
143

144
    args = docopt.docopt(__doc__)
×
145

146
    config_file: str = args["--config"]
×
147
    apply_mode: bool = args["--apply"]
×
148
    projects: list[str] | None = args["--project"] if args["--project"] else None
×
149
    config_types: list[str] | None = args["--type"] if args["--type"] else None
×
150
    show_diff: bool = args["--diff"]
×
151
    backup: bool = args["--backup"]
×
152
    verbose: bool = args["--verbose"]
×
153
    no_sync: bool = args["--no-sync"]
×
154
    git_commit_flag: bool = args["--git-commit"]
×
155
    git_push_flag: bool = args["--git-push"]
×
156
    validate_only: bool = args["--validate"]
×
157
    list_projects_flag: bool = args["--list-projects"]
×
158
    list_configs_flag: bool = args["--list-configs"]
×
159
    update_deps_flag: bool = args["--update-deps"]
×
NEW
160
    include_projects_flag: bool = args["--include-projects"]
×
NEW
161
    include_config_flag: bool = args["--include-config"]
×
162

163
    log_level = logging.DEBUG if verbose else logging.INFO
×
164
    my_lib.logger.init("py-project", level=log_level)
×
165

166
    console = rich.console.Console()
×
167

168
    # 設定タイプ一覧表示
169
    if list_configs_flag:
×
170
        show_config_types()
×
171
        sys.exit(0)
×
172

173
    # 設定ファイル読み込み
174
    try:
×
175
        config_dict = my_lib.config.load(config_file, pathlib.Path(_SCHEMA_CONFIG))
×
176
    except my_lib.config.ConfigFileNotFoundError as e:
×
177
        console.print(f"[red]設定ファイルが見つかりません: {e}[/red]")
×
178
        sys.exit(1)
×
179
    except my_lib.config.ConfigParseError as e:
×
180
        console.print(f"[red]設定ファイルの形式が不正です:[/red]\n{e.details}")
×
181
        sys.exit(1)
×
182
    except my_lib.config.ConfigValidationError as e:
×
183
        console.print(f"[red]設定ファイルの検証に失敗しました:[/red]\n{e.details}")
×
184
        sys.exit(1)
×
185

186
    # 検証のみ
187
    if validate_only:
×
188
        console.print("[green]設定ファイルは正常です[/green]")
×
189
        sys.exit(0)
×
190

191
    # dict を Config オブジェクトに変換
192
    config = py_project.config.Config.from_dict(config_dict)
×
193

194
    # プロジェクト一覧表示
195
    if list_projects_flag:
×
196
        show_projects(config)
×
197
        sys.exit(0)
×
198

199
    # 依存関係更新
200
    if update_deps_flag:
×
NEW
201
        results: list[py_project.dep_updater.FileUpdateResult] = []
×
202

203
        # -p が指定された場合はプロジェクトのみ(テンプレートは更新しない)
NEW
204
        update_template = projects is None
×
205

206
        # テンプレートの dependency-groups.dev を更新
NEW
207
        if update_template:
×
NEW
208
            template_dir = config.get_template_dir()
×
NEW
209
            template_path = template_dir / "pyproject" / "sections.toml"
×
NEW
210
            console.print("[bold]━━━ テンプレート (sections.toml) ━━━[/bold]")
×
NEW
211
            py_project.dep_updater.update_template_deps(
×
212
                template_path=template_path,
213
                dry_run=not apply_mode,
214
                console=console,
215
            )
216

217
        # プロジェクトの dependencies を更新
NEW
218
        if include_projects_flag or projects is not None:
×
NEW
219
            target_projects = [p for p in config.projects if projects is None or p.name in projects]
×
220

NEW
221
            for proj in target_projects:
×
NEW
222
                result = py_project.dep_updater.update_project_deps(
×
223
                    project=proj,
224
                    dry_run=not apply_mode,
225
                    console=console,
226
                )
NEW
227
                if result is not None:
×
NEW
228
                    results.append(result)
×
229

230
        # config.yaml の extra_dev_deps を更新
NEW
231
        if include_config_flag:
×
NEW
232
            console.print("\n[bold]━━━ 設定ファイル (config.yaml) ━━━[/bold]")
×
NEW
233
            result = py_project.dep_updater.update_config_deps(
×
234
                config_path=pathlib.Path(config_file),
235
                projects=projects,
236
                dry_run=not apply_mode,
237
                console=console,
238
            )
NEW
239
            if result is not None:
×
NEW
240
                results.append(result)
×
241

242
        # 差分表示
NEW
243
        if show_diff and results:
×
NEW
244
            console.print("\n[bold]━━━ 差分 ━━━[/bold]\n")
×
NEW
245
            for result in results:
×
NEW
246
                diff_text = py_project.dep_updater.format_diff(result)
×
NEW
247
                console.print(diff_text)
×
NEW
248
                console.print()
×
249

250
        # サマリー
NEW
251
        console.print()
×
NEW
252
        if not apply_mode:
×
NEW
253
            total_updates = sum(sum(1 for u in r.updates if u.updated) for r in results)
×
NEW
254
            if total_updates > 0:
×
NEW
255
                console.print(f"[yellow]🔍 合計 {total_updates} 個の依存関係が更新可能です[/yellow]")
×
NEW
256
                console.print("[dim]--apply を指定すると実際に更新されます[/dim]")
×
257

UNCOV
258
        sys.exit(0)
×
259

260
    # 設定適用
261
    options = py_project.config.ApplyOptions(
×
262
        dry_run=not apply_mode,
263
        backup=backup,
264
        show_diff=show_diff,
265
        run_sync=not no_sync,
266
        git_commit=git_commit_flag,
267
        git_push=git_push_flag,
268
    )
269
    ret_code = execute(
×
270
        config=config,
271
        options=options,
272
        projects=projects,
273
        config_types=config_types,
274
    )
275

276
    sys.exit(ret_code)
×
277

278

279
if __name__ == "__main__":
280
    main()
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc