tgit 0.34.2__tar.gz → 0.35.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.
- {tgit-0.34.2 → tgit-0.35.0}/CHANGELOG.md +12 -0
- {tgit-0.34.2 → tgit-0.35.0}/PKG-INFO +1 -1
- {tgit-0.34.2 → tgit-0.35.0}/pyproject.toml +1 -1
- {tgit-0.34.2 → tgit-0.35.0}/tests/integration/test_version_integration.py +104 -0
- {tgit-0.34.2 → tgit-0.35.0}/tests/unit/test_version.py +232 -23
- {tgit-0.34.2 → tgit-0.35.0}/tests/unit/test_version_coverage.py +2 -4
- {tgit-0.34.2 → tgit-0.35.0}/tgit/prompts/commit.txt +22 -6
- {tgit-0.34.2 → tgit-0.35.0}/tgit/version.py +229 -29
- {tgit-0.34.2 → tgit-0.35.0}/uv.lock +221 -244
- {tgit-0.34.2 → tgit-0.35.0}/.github/workflows/build.yml +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/.github/workflows/ci.yml +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/.github/workflows/claude-code-review.yml +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/.github/workflows/claude.yml +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/.gitignore +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/.python-version +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/.tgit/settings.json +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/.vscode/extensions.json +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/.vscode/launch.json +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/AGENTS.md +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/CLAUDE.md +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/LICENSE +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/README.md +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/pyrightconfig.json +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/scripts/publish.sh +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/scripts/test.sh +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/tests/README.md +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/tests/__init__.py +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/tests/conftest.py +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/tests/integration/__init__.py +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/tests/unit/__init__.py +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/tests/unit/test_add.py +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/tests/unit/test_changelog.py +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/tests/unit/test_changelog_coverage.py +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/tests/unit/test_cli.py +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/tests/unit/test_commit.py +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/tests/unit/test_commit_coverage.py +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/tests/unit/test_interactive_settings.py +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/tests/unit/test_settings.py +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/tests/unit/test_types.py +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/tests/unit/test_utils.py +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/tests/unit/test_utils_coverage.py +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/tgit/__init__.py +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/tgit/add.py +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/tgit/changelog.py +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/tgit/cli.py +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/tgit/commit.py +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/tgit/constants.py +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/tgit/interactive_settings.py +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/tgit/py.typed +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/tgit/settings.py +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/tgit/shared.py +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/tgit/types.py +0 -0
- {tgit-0.34.2 → tgit-0.35.0}/tgit/utils/__init__.py +0 -0
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## v0.35.0
|
|
2
|
+
|
|
3
|
+
[v0.34.2...v0.35.0](https://github.com/Jannchie/tgit/compare/v0.34.2...v0.35.0)
|
|
4
|
+
|
|
5
|
+
### :sparkles: Features
|
|
6
|
+
|
|
7
|
+
- **version**: add support for syncing Cargo.lock with Cargo.toml - By [Jannchie](mailto:jannchie@gmail.com) in [7ca29ae](https://github.com/Jannchie/tgit/commit/7ca29ae)
|
|
8
|
+
|
|
9
|
+
### :wrench: Chores
|
|
10
|
+
|
|
11
|
+
- update risk detection guidelines - By [Jannchie](mailto:jannchie@gmail.com) in [b5e0762](https://github.com/Jannchie/tgit/commit/b5e0762)
|
|
12
|
+
|
|
1
13
|
## v0.34.2
|
|
2
14
|
|
|
3
15
|
[v0.34.1...v0.34.2](https://github.com/Jannchie/tgit/compare/v0.34.1...v0.34.2)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tgit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.35.0
|
|
4
4
|
Summary: AI-powered Git CLI for commits, changelogs, and semantic versioning.
|
|
5
5
|
Project-URL: Homepage, https://github.com/Jannchie/tgit
|
|
6
6
|
Project-URL: Repository, https://github.com/Jannchie/tgit
|
|
@@ -305,6 +305,110 @@ description = "Test package"
|
|
|
305
305
|
node_modules_content = json.loads(node_modules_package_json.read_text())
|
|
306
306
|
assert node_modules_content["version"] == "1.0.0"
|
|
307
307
|
|
|
308
|
+
def _build_args(self, tmp_path, *, recursive: bool) -> VersionArgs:
|
|
309
|
+
return VersionArgs(
|
|
310
|
+
version="",
|
|
311
|
+
verbose=0,
|
|
312
|
+
no_commit=True,
|
|
313
|
+
no_tag=True,
|
|
314
|
+
no_push=True,
|
|
315
|
+
patch=False,
|
|
316
|
+
minor=False,
|
|
317
|
+
major=False,
|
|
318
|
+
prepatch="",
|
|
319
|
+
preminor="",
|
|
320
|
+
premajor="",
|
|
321
|
+
recursive=recursive,
|
|
322
|
+
custom="",
|
|
323
|
+
path=str(tmp_path),
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
def test_update_version_files_syncs_cargo_lock(self, tmp_path):
|
|
327
|
+
"""End-to-end: bumping a crate's Cargo.toml also rewrites its
|
|
328
|
+
Cargo.lock entry. The lockfile lives next to the manifest."""
|
|
329
|
+
(tmp_path / "Cargo.toml").write_text(
|
|
330
|
+
'[package]\nname = "demo"\nversion = "0.1.0"\n',
|
|
331
|
+
encoding="utf-8",
|
|
332
|
+
)
|
|
333
|
+
(tmp_path / "Cargo.lock").write_text(
|
|
334
|
+
'[[package]]\nname = "demo"\nversion = "0.1.0"\n',
|
|
335
|
+
encoding="utf-8",
|
|
336
|
+
)
|
|
337
|
+
update_version_files(
|
|
338
|
+
self._build_args(tmp_path, recursive=False),
|
|
339
|
+
Version(major=0, minor=2, patch=0),
|
|
340
|
+
verbose=0,
|
|
341
|
+
recursive=False,
|
|
342
|
+
)
|
|
343
|
+
assert 'version = "0.2.0"' in (tmp_path / "Cargo.toml").read_text()
|
|
344
|
+
assert 'version = "0.2.0"' in (tmp_path / "Cargo.lock").read_text()
|
|
345
|
+
|
|
346
|
+
def test_update_version_files_workspace_lockfile_dedups(self, tmp_path):
|
|
347
|
+
"""Cargo workspace with two members and one shared lockfile —
|
|
348
|
+
both [[package]] entries get rewritten, but the lockfile is
|
|
349
|
+
not duplicated."""
|
|
350
|
+
(tmp_path / ".git").mkdir()
|
|
351
|
+
(tmp_path / "Cargo.toml").write_text(
|
|
352
|
+
'[workspace]\nmembers = ["crates/alpha", "crates/beta"]\n',
|
|
353
|
+
encoding="utf-8",
|
|
354
|
+
)
|
|
355
|
+
(tmp_path / "Cargo.lock").write_text(
|
|
356
|
+
'[[package]]\nname = "alpha"\nversion = "0.1.0"\n\n'
|
|
357
|
+
'[[package]]\nname = "beta"\nversion = "0.1.0"\n',
|
|
358
|
+
encoding="utf-8",
|
|
359
|
+
)
|
|
360
|
+
alpha = tmp_path / "crates" / "alpha"
|
|
361
|
+
alpha.mkdir(parents=True)
|
|
362
|
+
(alpha / "Cargo.toml").write_text(
|
|
363
|
+
'[package]\nname = "alpha"\nversion = "0.1.0"\n',
|
|
364
|
+
encoding="utf-8",
|
|
365
|
+
)
|
|
366
|
+
beta = tmp_path / "crates" / "beta"
|
|
367
|
+
beta.mkdir(parents=True)
|
|
368
|
+
(beta / "Cargo.toml").write_text(
|
|
369
|
+
'[package]\nname = "beta"\nversion = "0.1.0"\n',
|
|
370
|
+
encoding="utf-8",
|
|
371
|
+
)
|
|
372
|
+
update_version_files(
|
|
373
|
+
self._build_args(tmp_path, recursive=True),
|
|
374
|
+
Version(major=0, minor=2, patch=0),
|
|
375
|
+
verbose=0,
|
|
376
|
+
recursive=True,
|
|
377
|
+
)
|
|
378
|
+
lockfile_content = (tmp_path / "Cargo.lock").read_text()
|
|
379
|
+
assert lockfile_content.count('version = "0.2.0"') == 2
|
|
380
|
+
assert 'version = "0.1.0"' not in lockfile_content
|
|
381
|
+
|
|
382
|
+
def test_update_version_files_pnpm_root_without_version(self, tmp_path):
|
|
383
|
+
"""pnpm workspace root without `version`: bump must insert
|
|
384
|
+
one in the root package.json and update children too."""
|
|
385
|
+
(tmp_path / "package.json").write_text(
|
|
386
|
+
'{\n "name": "monorepo",\n "private": true\n}\n',
|
|
387
|
+
encoding="utf-8",
|
|
388
|
+
)
|
|
389
|
+
(tmp_path / "pnpm-workspace.yaml").write_text(
|
|
390
|
+
'packages:\n - "packages/*"\n',
|
|
391
|
+
encoding="utf-8",
|
|
392
|
+
)
|
|
393
|
+
alpha = tmp_path / "packages" / "alpha"
|
|
394
|
+
alpha.mkdir(parents=True)
|
|
395
|
+
(alpha / "package.json").write_text(
|
|
396
|
+
'{"name": "@x/alpha", "version": "0.1.0"}\n',
|
|
397
|
+
encoding="utf-8",
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
update_version_files(
|
|
401
|
+
self._build_args(tmp_path, recursive=True),
|
|
402
|
+
Version(major=0, minor=2, patch=0),
|
|
403
|
+
verbose=0,
|
|
404
|
+
recursive=True,
|
|
405
|
+
)
|
|
406
|
+
root_data = json.loads((tmp_path / "package.json").read_text())
|
|
407
|
+
assert root_data["version"] == "0.2.0"
|
|
408
|
+
assert root_data["name"] == "monorepo"
|
|
409
|
+
child_data = json.loads((alpha / "package.json").read_text())
|
|
410
|
+
assert child_data["version"] == "0.2.0"
|
|
411
|
+
|
|
308
412
|
@patch("tgit.version.get_next_version")
|
|
309
413
|
@patch("tgit.version.get_current_version")
|
|
310
414
|
@patch("tgit.version.update_version_files")
|
|
@@ -19,8 +19,11 @@ from tgit.version import (
|
|
|
19
19
|
_parse_gitignore,
|
|
20
20
|
_prompt_for_version_choice,
|
|
21
21
|
_should_ignore_path,
|
|
22
|
+
_version_from_workspace_children,
|
|
22
23
|
bump_version,
|
|
24
|
+
cargo_lockfiles_for_manifests,
|
|
23
25
|
execute_git_commands,
|
|
26
|
+
find_workspace_package_jsons,
|
|
24
27
|
format_diff_lines,
|
|
25
28
|
get_current_version,
|
|
26
29
|
get_custom_version,
|
|
@@ -39,9 +42,11 @@ from tgit.version import (
|
|
|
39
42
|
get_version_from_version_txt,
|
|
40
43
|
handle_version,
|
|
41
44
|
show_file_diff,
|
|
45
|
+
sync_cargo_lockfiles,
|
|
42
46
|
update_cargo_lock_version,
|
|
43
47
|
update_cargo_toml_version,
|
|
44
48
|
update_file,
|
|
49
|
+
update_package_json_version,
|
|
45
50
|
update_version_files,
|
|
46
51
|
update_version_in_file,
|
|
47
52
|
version,
|
|
@@ -1110,37 +1115,32 @@ class TestUpdateCargoLockVersion:
|
|
|
1110
1115
|
assert lockfile.stat().st_mtime_ns == before
|
|
1111
1116
|
|
|
1112
1117
|
|
|
1113
|
-
class
|
|
1114
|
-
"""
|
|
1118
|
+
class TestSyncCargoLockfiles:
|
|
1119
|
+
"""sync_cargo_lockfiles is the post-manifest step that rewrites
|
|
1120
|
+
every Cargo.lock entry matching a bumped manifest. Manifest update
|
|
1121
|
+
and lockfile sync are deliberately decoupled so workspace members
|
|
1122
|
+
sharing one lockfile don't trigger N rewrites of it."""
|
|
1115
1123
|
|
|
1116
|
-
def
|
|
1124
|
+
def test_syncs_sibling_lockfile(self, tmp_path):
|
|
1117
1125
|
cargo_toml = tmp_path / "Cargo.toml"
|
|
1118
1126
|
cargo_toml.write_text(
|
|
1119
1127
|
"[package]\n"
|
|
1120
1128
|
'name = "arthash"\n'
|
|
1121
|
-
'version = "0.
|
|
1122
|
-
"\n"
|
|
1123
|
-
"[dependencies]\n"
|
|
1124
|
-
'matrixmultiply = "0.3"\n',
|
|
1129
|
+
'version = "0.3.0"\n',
|
|
1125
1130
|
encoding="utf-8",
|
|
1126
1131
|
)
|
|
1127
1132
|
lockfile = tmp_path / "Cargo.lock"
|
|
1128
1133
|
lockfile.write_text(
|
|
1129
1134
|
"[[package]]\n"
|
|
1130
1135
|
'name = "arthash"\n'
|
|
1131
|
-
'version = "0.2.0"\n'
|
|
1132
|
-
"dependencies = [\n"
|
|
1133
|
-
' "matrixmultiply",\n'
|
|
1134
|
-
"]\n",
|
|
1136
|
+
'version = "0.2.0"\n',
|
|
1135
1137
|
encoding="utf-8",
|
|
1136
1138
|
)
|
|
1137
|
-
|
|
1138
|
-
# Both files should now report 0.3.0.
|
|
1139
|
-
assert 'version = "0.3.0"' in cargo_toml.read_text()
|
|
1139
|
+
sync_cargo_lockfiles([cargo_toml], "0.3.0", 0, show_diff=False)
|
|
1140
1140
|
assert 'version = "0.3.0"' in lockfile.read_text()
|
|
1141
1141
|
|
|
1142
|
-
def
|
|
1143
|
-
"""
|
|
1142
|
+
def test_syncs_workspace_root_lockfile(self, tmp_path):
|
|
1143
|
+
"""Workspace member's manifest, lockfile at the workspace root."""
|
|
1144
1144
|
(tmp_path / ".git").mkdir()
|
|
1145
1145
|
lockfile = tmp_path / "Cargo.lock"
|
|
1146
1146
|
lockfile.write_text(
|
|
@@ -1155,26 +1155,235 @@ class TestUpdateVersionInFileCargoIntegration:
|
|
|
1155
1155
|
cargo_toml.write_text(
|
|
1156
1156
|
"[package]\n"
|
|
1157
1157
|
'name = "alpha"\n'
|
|
1158
|
-
'version = "0.
|
|
1158
|
+
'version = "0.3.0"\n',
|
|
1159
1159
|
encoding="utf-8",
|
|
1160
1160
|
)
|
|
1161
|
-
|
|
1162
|
-
assert 'version = "0.3.0"' in cargo_toml.read_text()
|
|
1161
|
+
sync_cargo_lockfiles([cargo_toml], "0.3.0", 0, show_diff=False)
|
|
1163
1162
|
assert 'version = "0.3.0"' in lockfile.read_text()
|
|
1164
1163
|
|
|
1165
|
-
def
|
|
1166
|
-
"""
|
|
1164
|
+
def test_no_lockfile_present_is_noop(self, tmp_path):
|
|
1165
|
+
"""Pure non-Rust projects or fresh crates may lack a lockfile."""
|
|
1167
1166
|
(tmp_path / ".git").mkdir()
|
|
1168
1167
|
cargo_toml = tmp_path / "Cargo.toml"
|
|
1169
1168
|
cargo_toml.write_text(
|
|
1170
1169
|
"[package]\n"
|
|
1171
1170
|
'name = "alpha"\n'
|
|
1171
|
+
'version = "0.3.0"\n',
|
|
1172
|
+
encoding="utf-8",
|
|
1173
|
+
)
|
|
1174
|
+
# Should not raise.
|
|
1175
|
+
sync_cargo_lockfiles([cargo_toml], "0.3.0", 0, show_diff=False)
|
|
1176
|
+
|
|
1177
|
+
def test_workspace_members_dedup_to_one_lockfile_rewrite(self, tmp_path):
|
|
1178
|
+
"""Two members share one lockfile; both their entries must be
|
|
1179
|
+
rewritten, and the lockfile resolution must not duplicate."""
|
|
1180
|
+
(tmp_path / ".git").mkdir()
|
|
1181
|
+
lockfile = tmp_path / "Cargo.lock"
|
|
1182
|
+
lockfile.write_text(
|
|
1183
|
+
"[[package]]\n"
|
|
1184
|
+
'name = "alpha"\n'
|
|
1185
|
+
'version = "0.2.0"\n'
|
|
1186
|
+
"\n"
|
|
1187
|
+
"[[package]]\n"
|
|
1188
|
+
'name = "beta"\n'
|
|
1172
1189
|
'version = "0.2.0"\n',
|
|
1173
1190
|
encoding="utf-8",
|
|
1174
1191
|
)
|
|
1192
|
+
alpha_toml = tmp_path / "crates" / "alpha" / "Cargo.toml"
|
|
1193
|
+
alpha_toml.parent.mkdir(parents=True)
|
|
1194
|
+
alpha_toml.write_text(
|
|
1195
|
+
"[package]\n"
|
|
1196
|
+
'name = "alpha"\n'
|
|
1197
|
+
'version = "0.3.0"\n',
|
|
1198
|
+
encoding="utf-8",
|
|
1199
|
+
)
|
|
1200
|
+
beta_toml = tmp_path / "crates" / "beta" / "Cargo.toml"
|
|
1201
|
+
beta_toml.parent.mkdir(parents=True)
|
|
1202
|
+
beta_toml.write_text(
|
|
1203
|
+
"[package]\n"
|
|
1204
|
+
'name = "beta"\n'
|
|
1205
|
+
'version = "0.3.0"\n',
|
|
1206
|
+
encoding="utf-8",
|
|
1207
|
+
)
|
|
1208
|
+
sync_cargo_lockfiles([alpha_toml, beta_toml], "0.3.0", 0, show_diff=False)
|
|
1209
|
+
new_content = lockfile.read_text()
|
|
1210
|
+
assert new_content.count('version = "0.3.0"') == 2
|
|
1211
|
+
assert 'version = "0.2.0"' not in new_content
|
|
1212
|
+
|
|
1213
|
+
def test_manifests_without_package_name_skipped(self, tmp_path):
|
|
1214
|
+
"""A pure [workspace] manifest has no `name`; nothing to do."""
|
|
1215
|
+
(tmp_path / ".git").mkdir()
|
|
1216
|
+
ws_toml = tmp_path / "Cargo.toml"
|
|
1217
|
+
ws_toml.write_text('[workspace]\nmembers = ["crates/alpha"]\n', encoding="utf-8")
|
|
1218
|
+
lockfile = tmp_path / "Cargo.lock"
|
|
1219
|
+
original = '[[package]]\nname = "alpha"\nversion = "0.2.0"\n'
|
|
1220
|
+
lockfile.write_text(original, encoding="utf-8")
|
|
1221
|
+
sync_cargo_lockfiles([ws_toml], "0.3.0", 0, show_diff=False)
|
|
1222
|
+
assert lockfile.read_text() == original
|
|
1223
|
+
|
|
1224
|
+
|
|
1225
|
+
class TestCargoLockfilesForManifests:
|
|
1226
|
+
"""Resolution of unique Cargo.lock files for a manifest set."""
|
|
1227
|
+
|
|
1228
|
+
def test_dedups_shared_workspace_lockfile(self, tmp_path):
|
|
1229
|
+
(tmp_path / ".git").mkdir()
|
|
1230
|
+
lockfile = tmp_path / "Cargo.lock"
|
|
1231
|
+
lockfile.write_text("# lock\n")
|
|
1232
|
+
alpha = tmp_path / "crates" / "alpha" / "Cargo.toml"
|
|
1233
|
+
alpha.parent.mkdir(parents=True)
|
|
1234
|
+
alpha.write_text('[package]\nname = "alpha"\nversion = "0.1.0"\n')
|
|
1235
|
+
beta = tmp_path / "crates" / "beta" / "Cargo.toml"
|
|
1236
|
+
beta.parent.mkdir(parents=True)
|
|
1237
|
+
beta.write_text('[package]\nname = "beta"\nversion = "0.1.0"\n')
|
|
1238
|
+
result = cargo_lockfiles_for_manifests([alpha, beta])
|
|
1239
|
+
assert result == [lockfile]
|
|
1240
|
+
|
|
1241
|
+
def test_ignores_non_cargo_paths(self, tmp_path):
|
|
1242
|
+
assert cargo_lockfiles_for_manifests([tmp_path / "package.json"]) == []
|
|
1243
|
+
|
|
1244
|
+
def test_missing_lockfile_returns_empty(self, tmp_path):
|
|
1245
|
+
(tmp_path / ".git").mkdir()
|
|
1246
|
+
cargo_toml = tmp_path / "Cargo.toml"
|
|
1247
|
+
cargo_toml.write_text('[package]\nname = "x"\nversion = "0.1.0"\n')
|
|
1248
|
+
assert cargo_lockfiles_for_manifests([cargo_toml]) == []
|
|
1249
|
+
|
|
1250
|
+
|
|
1251
|
+
class TestUpdatePackageJsonVersion:
|
|
1252
|
+
"""update_package_json_version handles both replace (most pkgs) and
|
|
1253
|
+
insert (pnpm/npm workspace roots that omit `version`)."""
|
|
1254
|
+
|
|
1255
|
+
def test_replaces_existing_version(self, tmp_path):
|
|
1256
|
+
pkg = tmp_path / "package.json"
|
|
1257
|
+
pkg.write_text('{\n "name": "x",\n "version": "0.1.0"\n}\n', encoding="utf-8")
|
|
1258
|
+
update_package_json_version(str(pkg), "0.2.0", 0, show_diff=False)
|
|
1259
|
+
assert '"version": "0.2.0"' in pkg.read_text()
|
|
1260
|
+
assert '"version": "0.1.0"' not in pkg.read_text()
|
|
1261
|
+
|
|
1262
|
+
def test_inserts_when_missing(self, tmp_path):
|
|
1263
|
+
"""Workspace root without `version` — must end up with one
|
|
1264
|
+
and remain valid JSON."""
|
|
1265
|
+
pkg = tmp_path / "package.json"
|
|
1266
|
+
pkg.write_text(
|
|
1267
|
+
'{\n "name": "monorepo",\n "private": true,\n "workspaces": ["packages/*"]\n}\n',
|
|
1268
|
+
encoding="utf-8",
|
|
1269
|
+
)
|
|
1270
|
+
update_package_json_version(str(pkg), "1.0.0", 0, show_diff=False)
|
|
1271
|
+
new_content = pkg.read_text()
|
|
1272
|
+
assert '"version": "1.0.0"' in new_content
|
|
1273
|
+
# Still valid JSON
|
|
1274
|
+
import json as _json
|
|
1275
|
+
data = _json.loads(new_content)
|
|
1276
|
+
assert data["version"] == "1.0.0"
|
|
1277
|
+
assert data["name"] == "monorepo"
|
|
1278
|
+
|
|
1279
|
+
def test_only_first_version_match_replaced(self, tmp_path):
|
|
1280
|
+
"""Avoid touching a nested `"version"` inside dependencies."""
|
|
1281
|
+
pkg = tmp_path / "package.json"
|
|
1282
|
+
pkg.write_text(
|
|
1283
|
+
'{\n'
|
|
1284
|
+
' "name": "x",\n'
|
|
1285
|
+
' "version": "0.1.0",\n'
|
|
1286
|
+
' "dependencies": {\n'
|
|
1287
|
+
' "left-pad": "1.3.0"\n'
|
|
1288
|
+
' }\n'
|
|
1289
|
+
'}\n',
|
|
1290
|
+
encoding="utf-8",
|
|
1291
|
+
)
|
|
1292
|
+
update_package_json_version(str(pkg), "0.2.0", 0, show_diff=False)
|
|
1293
|
+
new_content = pkg.read_text()
|
|
1294
|
+
assert '"version": "0.2.0"' in new_content
|
|
1295
|
+
# left-pad pin must remain unchanged
|
|
1296
|
+
assert '"left-pad": "1.3.0"' in new_content
|
|
1297
|
+
|
|
1298
|
+
def test_file_not_exists_is_noop(self, tmp_path):
|
|
1175
1299
|
# Should not raise.
|
|
1176
|
-
|
|
1177
|
-
|
|
1300
|
+
update_package_json_version(str(tmp_path / "missing.json"), "1.0.0", 0, show_diff=False)
|
|
1301
|
+
|
|
1302
|
+
|
|
1303
|
+
class TestWorkspaceVersionDiscovery:
|
|
1304
|
+
"""When the root package.json omits `version` (typical pnpm/yarn
|
|
1305
|
+
monorepo root), version detection falls back to the highest
|
|
1306
|
+
version among workspace children."""
|
|
1307
|
+
|
|
1308
|
+
def test_pnpm_workspace_yaml_resolves_child_versions(self, tmp_path):
|
|
1309
|
+
(tmp_path / "package.json").write_text('{"name": "root", "private": true}\n', encoding="utf-8")
|
|
1310
|
+
(tmp_path / "pnpm-workspace.yaml").write_text('packages:\n - "packages/*"\n', encoding="utf-8")
|
|
1311
|
+
alpha = tmp_path / "packages" / "alpha"
|
|
1312
|
+
alpha.mkdir(parents=True)
|
|
1313
|
+
(alpha / "package.json").write_text('{"name": "@x/alpha", "version": "1.2.3"}\n', encoding="utf-8")
|
|
1314
|
+
beta = tmp_path / "packages" / "beta"
|
|
1315
|
+
beta.mkdir(parents=True)
|
|
1316
|
+
(beta / "package.json").write_text('{"name": "@x/beta", "version": "1.5.0"}\n', encoding="utf-8")
|
|
1317
|
+
|
|
1318
|
+
version = get_version_from_package_json(tmp_path)
|
|
1319
|
+
assert version is not None
|
|
1320
|
+
# Max of child versions
|
|
1321
|
+
assert (version.major, version.minor, version.patch) == (1, 5, 0)
|
|
1322
|
+
|
|
1323
|
+
def test_npm_workspaces_field_list(self, tmp_path):
|
|
1324
|
+
(tmp_path / "package.json").write_text(
|
|
1325
|
+
'{"name": "root", "private": true, "workspaces": ["packages/*"]}\n',
|
|
1326
|
+
encoding="utf-8",
|
|
1327
|
+
)
|
|
1328
|
+
alpha = tmp_path / "packages" / "alpha"
|
|
1329
|
+
alpha.mkdir(parents=True)
|
|
1330
|
+
(alpha / "package.json").write_text('{"name": "@x/alpha", "version": "0.4.0"}\n', encoding="utf-8")
|
|
1331
|
+
|
|
1332
|
+
version = get_version_from_package_json(tmp_path)
|
|
1333
|
+
assert version is not None
|
|
1334
|
+
assert (version.major, version.minor, version.patch) == (0, 4, 0)
|
|
1335
|
+
|
|
1336
|
+
def test_yarn_workspaces_object_form(self, tmp_path):
|
|
1337
|
+
(tmp_path / "package.json").write_text(
|
|
1338
|
+
'{"name": "root", "workspaces": {"packages": ["modules/*"]}}\n',
|
|
1339
|
+
encoding="utf-8",
|
|
1340
|
+
)
|
|
1341
|
+
alpha = tmp_path / "modules" / "alpha"
|
|
1342
|
+
alpha.mkdir(parents=True)
|
|
1343
|
+
(alpha / "package.json").write_text('{"name": "@x/alpha", "version": "2.0.1"}\n', encoding="utf-8")
|
|
1344
|
+
|
|
1345
|
+
version = get_version_from_package_json(tmp_path)
|
|
1346
|
+
assert version is not None
|
|
1347
|
+
assert (version.major, version.minor, version.patch) == (2, 0, 1)
|
|
1348
|
+
|
|
1349
|
+
def test_root_version_takes_precedence(self, tmp_path):
|
|
1350
|
+
"""If root has its own version, children are ignored."""
|
|
1351
|
+
(tmp_path / "package.json").write_text(
|
|
1352
|
+
'{"name": "root", "version": "5.0.0", "workspaces": ["packages/*"]}\n',
|
|
1353
|
+
encoding="utf-8",
|
|
1354
|
+
)
|
|
1355
|
+
alpha = tmp_path / "packages" / "alpha"
|
|
1356
|
+
alpha.mkdir(parents=True)
|
|
1357
|
+
(alpha / "package.json").write_text('{"version": "9.9.9"}\n', encoding="utf-8")
|
|
1358
|
+
version = get_version_from_package_json(tmp_path)
|
|
1359
|
+
assert version is not None
|
|
1360
|
+
assert (version.major, version.minor, version.patch) == (5, 0, 0)
|
|
1361
|
+
|
|
1362
|
+
def test_no_workspace_no_version_returns_none(self, tmp_path):
|
|
1363
|
+
(tmp_path / "package.json").write_text('{"name": "x"}\n', encoding="utf-8")
|
|
1364
|
+
assert get_version_from_package_json(tmp_path) is None
|
|
1365
|
+
|
|
1366
|
+
def test_child_without_version_skipped(self, tmp_path):
|
|
1367
|
+
(tmp_path / "package.json").write_text('{"workspaces": ["packages/*"]}\n', encoding="utf-8")
|
|
1368
|
+
alpha = tmp_path / "packages" / "alpha"
|
|
1369
|
+
alpha.mkdir(parents=True)
|
|
1370
|
+
(alpha / "package.json").write_text('{"name": "@x/alpha"}\n', encoding="utf-8")
|
|
1371
|
+
beta = tmp_path / "packages" / "beta"
|
|
1372
|
+
beta.mkdir(parents=True)
|
|
1373
|
+
(beta / "package.json").write_text('{"name": "@x/beta", "version": "0.1.0"}\n', encoding="utf-8")
|
|
1374
|
+
|
|
1375
|
+
version = get_version_from_package_json(tmp_path)
|
|
1376
|
+
assert version is not None
|
|
1377
|
+
assert (version.major, version.minor, version.patch) == (0, 1, 0)
|
|
1378
|
+
|
|
1379
|
+
def test_find_workspace_package_jsons_excludes_root(self, tmp_path):
|
|
1380
|
+
(tmp_path / "package.json").write_text('{"workspaces": ["."]}\n', encoding="utf-8")
|
|
1381
|
+
result = find_workspace_package_jsons(tmp_path)
|
|
1382
|
+
assert result == []
|
|
1383
|
+
|
|
1384
|
+
def test_version_from_workspace_children_no_workspace(self, tmp_path):
|
|
1385
|
+
"""No workspaces config at all → no children, returns None."""
|
|
1386
|
+
assert _version_from_workspace_children(tmp_path) is None
|
|
1178
1387
|
|
|
1179
1388
|
|
|
1180
1389
|
class TestParseGitignore:
|
|
@@ -311,14 +311,12 @@ version = "invalid"
|
|
|
311
311
|
str(file_path), r'version\s*=\s*".*?"', 'version = "1.2.3"', 0, show_diff=False
|
|
312
312
|
)
|
|
313
313
|
|
|
314
|
-
@patch("tgit.version.
|
|
314
|
+
@patch("tgit.version.update_package_json_version")
|
|
315
315
|
def test_update_version_in_file_package_json(self, mock_update, tmp_path):
|
|
316
316
|
"""Test update_version_in_file for package.json."""
|
|
317
317
|
file_path = tmp_path / "package.json"
|
|
318
318
|
update_version_in_file(0, "1.2.3", "package.json", file_path)
|
|
319
|
-
mock_update.assert_called_once_with(
|
|
320
|
-
str(file_path), r'"version":\s*".*?"', '"version": "1.2.3"', 0, show_diff=False
|
|
321
|
-
)
|
|
319
|
+
mock_update.assert_called_once_with(str(file_path), "1.2.3", 0, show_diff=False)
|
|
322
320
|
|
|
323
321
|
@patch("tgit.version.update_file")
|
|
324
322
|
def test_update_version_in_file_version_txt(self, mock_update, tmp_path):
|
|
@@ -65,12 +65,27 @@ Select the most appropriate type based on the semantic versioning rules above.
|
|
|
65
65
|
- Cover the primary change(s) in the diff
|
|
66
66
|
- If multiple distinct changes, separate with " && " (e.g., "update api && fix validation")
|
|
67
67
|
|
|
68
|
-
###
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
68
|
+
### Risky Content & Files Detection
|
|
69
|
+
Review the diff and the list of changed files for anything that should not normally be committed. For every finding, append an entry to the `secrets` array with the file path, a brief description, and a `level` (`warning` or `error`). Return an empty array if nothing is found.
|
|
70
|
+
|
|
71
|
+
Flag two broad categories:
|
|
72
|
+
|
|
73
|
+
**1. Secrets / credentials embedded in code or config**
|
|
74
|
+
- `level: "error"` when an actual secret value appears: tokens, passwords, private keys, credentials, connection strings, OAuth client secrets, or high-entropy strings that look like generated keys.
|
|
75
|
+
- `level: "warning"` when only key names or variable names appear without values (e.g., `API_KEY=`, `SECRET_KEY=`, `PASSWORD=`).
|
|
76
|
+
|
|
77
|
+
**2. Files that should not be tracked in version control**
|
|
78
|
+
Flag these as `level: "error"` (description should explain what kind of file it is and why it shouldn't be committed). Use judgment based on filename, file path, and content:
|
|
79
|
+
- Log files: `*.log`, `npm-debug.log*`, `yarn-error.log*`, `pnpm-debug.log*`, anything under a `logs/` directory.
|
|
80
|
+
- Local/runtime environment files: `.env`, `.env.local`, `.env.*.local`, `.env.development.local`, `.env.production.local`. (Example/template env files like `.env.example`, `.env.sample`, `.env.template` are fine — do NOT flag them.)
|
|
81
|
+
- Private key / credential files: `*.pem`, `*.key`, `*.p12`, `*.pfx`, `id_rsa`, `id_ed25519`, `id_dsa`, `id_ecdsa`, `credentials.json`, `secrets.json`, `secrets.yaml`, `secrets.yml`.
|
|
82
|
+
- OS metadata: `.DS_Store`, `Thumbs.db`, `desktop.ini`, `ehthumbs.db`.
|
|
83
|
+
- Editor swap / backup files: `*.swp`, `*.swo`, `*~`, `*.orig`, `*.bak`, `*.rej`.
|
|
84
|
+
- Dependency / build / cache directories that virtually never belong in source control: `node_modules/`, `__pycache__/`, `*.pyc`, `*.pyo`, `.pytest_cache/`, `.mypy_cache/`, `.ruff_cache/`, `.tox/`, `.coverage`, `htmlcov/`, `*.egg-info/`, `.next/`, `.nuxt/`, `.turbo/`, `.parcel-cache/`, `.venv/`, `venv/`, `dist/`, `build/`, `target/debug/`, `target/release/`, `coverage/`.
|
|
85
|
+
- Runtime databases / data dumps that look like artifacts: `*.sqlite`, `*.sqlite3`, `*.db`, large data dumps in obvious artifact locations.
|
|
86
|
+
- Generic temp files: `*.tmp`, `*.temp`, core dumps (`core.*`).
|
|
87
|
+
|
|
88
|
+
Apply judgment — these patterns are heuristics, not absolute rules. If a file matches a pattern but the project genuinely tracks it on purpose (e.g., a fixture SQLite under `tests/fixtures/`, a vendored binary, a checked-in `dist/` for a published artifact), do not flag it. Conversely, flag files that clearly look like artifacts even if they don't match the patterns above.
|
|
74
89
|
|
|
75
90
|
## Output Format
|
|
76
91
|
Return valid JSON matching this structure:
|
|
@@ -97,6 +112,7 @@ Return valid JSON matching this structure:
|
|
|
97
112
|
{"type": "refactor", "scope": "api", "msg": "restructure endpoint handlers", "is_breaking": false, "secrets": []}
|
|
98
113
|
{"type": "feat", "scope": "api", "msg": "add user deletion endpoint", "is_breaking": true, "secrets": []}
|
|
99
114
|
{"type": "chore", "scope": "deps", "msg": "update dependencies", "is_breaking": false, "secrets": [{"file": "config/.env", "description": "detected value resembling api key", "level": "error"}]}
|
|
115
|
+
{"type": "chore", "scope": null, "msg": "update build pipeline", "is_breaking": false, "secrets": [{"file": "logs/server.log", "description": "log file should not be committed", "level": "error"}, {"file": "node_modules/lodash/package.json", "description": "node_modules dependency directory should not be committed", "level": "error"}]}
|
|
100
116
|
```
|
|
101
117
|
|
|
102
118
|
Now analyze the provided diff and generate the commit message.
|