python-kacl 0.4.6__py3-none-any.whl → 0.5.0__py3-none-any.whl

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.
kacl/__init__.py CHANGED
@@ -1,5 +1,5 @@
1
1
  # Version of the python-kacl package
2
- __version__ = "0.4.6"
2
+ __version__ = "0.5.0"
3
3
 
4
4
  from kacl.document import KACLDocument
5
5
  from kacl.serializer import KACLMarkdownSerializer
kacl/document.py CHANGED
@@ -220,7 +220,14 @@ class KACLDocument:
220
220
  self.__versions.insert(0, unreleased_version)
221
221
  unreleased_version.add(section.capitalize(), data)
222
222
 
223
- def release(self, version=None, link=None, auto_link=False, increment=None):
223
+ def release(
224
+ self,
225
+ version=None,
226
+ link=None,
227
+ auto_link=False,
228
+ increment=None,
229
+ allow_no_changes=False,
230
+ ):
224
231
  """Creates a new release version by copying the 'unreleased' changes into the
225
232
  new version
226
233
 
@@ -228,24 +235,14 @@ class KACLDocument:
228
235
  link {[str]} -- url the version will be linked with (default: {None})
229
236
  version {[str]} -- semantic versioning string
230
237
  increment {[str]} -- use either 'patch', 'minor', or 'major' to automatically increment the last version
238
+ allow_no_changes {[bool]} -- allow releasing a version without changes (default: {False})
231
239
  """
240
+
241
+ # check if 'post_release_version_prefix' is set
242
+ post_release_version_prefix = self.config.post_release_version_prefix
243
+
232
244
  if increment:
233
- v = self.current_version()
234
- if v:
235
- sv = semver.VersionInfo.parse(v)
236
- if "post" == increment:
237
- sv = sv.bump_prerelease(token="post")
238
- elif "patch" == increment:
239
- sv = sv.bump_patch()
240
- elif "minor" == increment:
241
- sv = sv.bump_minor()
242
- elif "major" == increment:
243
- sv = sv.bump_major()
244
- version = str(sv)
245
- else:
246
- raise KACLException(
247
- "No previously released version found. Incrementing not possible"
248
- )
245
+ version = self.next_version(increment=increment)
249
246
 
250
247
  # check that version is a valid semantic version
251
248
  future_version = semver.VersionInfo.parse(
@@ -253,7 +250,7 @@ class KACLDocument:
253
250
  ) # --> will throw a ValueError if version is not a valid semver
254
251
 
255
252
  # check if there are changes to release
256
- if self.has_changes() is False:
253
+ if not allow_no_changes and not self.has_changes():
257
254
  raise KACLException(
258
255
  "The current changelog has no changes. You can only release if changes are available."
259
256
  )
@@ -282,8 +279,6 @@ class KACLDocument:
282
279
  patch=future_version.patch,
283
280
  )
284
281
 
285
- # check if 'post_release_version_prefix' is set
286
- post_release_version_prefix = self.config.post_release_version_prefix
287
282
  comp_result = 0
288
283
  if post_release_version_prefix:
289
284
  # 2.1 Check if 'future version' is a 'post version'
@@ -376,6 +371,36 @@ class KACLDocument:
376
371
  if res and len(res):
377
372
  return res[0]
378
373
 
374
+ def next_version(self, increment="patch"):
375
+ """returns the current version (last released)
376
+ increment {[str]} -- use either 'patch', 'minor', or 'major' to automatically increment the last version
377
+ Returns:
378
+ [str] -- latest released version, None if none is available
379
+ """
380
+ v = self.current_version()
381
+ next_version = None
382
+ if v:
383
+ sv = semver.VersionInfo.parse(v)
384
+ if "post" == increment:
385
+ if not self.config.post_release_version_prefix:
386
+ raise KACLException(
387
+ "No 'post_release_version_prefix' set in the config. Incrementing not possible"
388
+ )
389
+ sv = sv.bump_prerelease(token=self.config.post_release_version_prefix)
390
+ elif "patch" == increment:
391
+ sv = sv.bump_patch()
392
+ elif "minor" == increment:
393
+ sv = sv.bump_minor()
394
+ elif "major" == increment:
395
+ sv = sv.bump_major()
396
+ next_version = str(sv)
397
+ else:
398
+ raise KACLException(
399
+ "No previously released version found. Incrementing not possible"
400
+ )
401
+
402
+ return next_version
403
+
379
404
  def current_version(self):
380
405
  """returns the current version (last released)
381
406
 
kacl/kacl_cli.py CHANGED
@@ -15,6 +15,35 @@ from kacl.config import KACLConfig
15
15
  from kacl.exception import KACLException
16
16
 
17
17
 
18
+ def check_auto_increment(version):
19
+ # check if the version string indicates automatic increment
20
+ increment = None
21
+ if version in ["major", "minor", "patch", "post"]:
22
+ increment = version
23
+ version = None # reset
24
+ else:
25
+ # check if version is a valid semantic version
26
+ try:
27
+ semver.VersionInfo.parse(version)
28
+ except Exception:
29
+ click.echo(
30
+ click.style("Error: ", fg="red")
31
+ + f'"{version}" not a valid semantic version.'
32
+ )
33
+ sys.exit(1)
34
+
35
+ return version, increment
36
+
37
+
38
+ def raise_on_invalid(kacl_changelog):
39
+ if not kacl_changelog.is_valid():
40
+ click.echo(
41
+ click.style("Error: ", fg="red")
42
+ + "Changelog is not valid. Run 'kacl-cli verify' for more information."
43
+ )
44
+ sys.exit(1)
45
+
46
+
18
47
  def load_changelog(ctx):
19
48
  config_file_path = ctx.obj["config"]
20
49
  input_file = ctx.obj["file"]
@@ -124,6 +153,27 @@ def current(ctx):
124
153
  click.echo(current_version)
125
154
 
126
155
 
156
+ @cli.command()
157
+ @click.pass_context
158
+ @click.argument("version", type=str)
159
+ def next(ctx, version):
160
+ """Returns the next version based on the current version from the Changelog."""
161
+ kacl_changelog = load_changelog(ctx)
162
+ raise_on_invalid(kacl_changelog)
163
+
164
+ version, increment = check_auto_increment(version)
165
+
166
+ if increment:
167
+ version = kacl_changelog.next_version(increment=increment)
168
+
169
+ # check that version is a valid semantic version
170
+ future_version = semver.VersionInfo.parse(
171
+ version
172
+ ) # --> will throw a ValueError if version is not a valid semver
173
+
174
+ click.echo(future_version)
175
+
176
+
127
177
  @cli.command()
128
178
  @click.pass_context
129
179
  @click.argument("version", type=str)
@@ -343,6 +393,11 @@ def verify(ctx, as_json):
343
393
  is_flag=True,
344
394
  help='If passed this will allow to commit/tag even on a "dirty".',
345
395
  )
396
+ @click.option(
397
+ "--allow-no-changes",
398
+ is_flag=True,
399
+ help="If passed this will allow to release without any changes.",
400
+ )
346
401
  def release(
347
402
  ctx,
348
403
  version,
@@ -356,6 +411,7 @@ def release(
356
411
  tag_name,
357
412
  tag_description,
358
413
  allow_dirty,
414
+ allow_no_changes,
359
415
  ):
360
416
  """Creates a release for the latest 'unreleased' changes. Use '--modify' to directly modify the changelog file.
361
417
  You can automatically use the latest version by using the version keywords 'major', 'minor', 'patch', 'post'
@@ -368,31 +424,11 @@ def release(
368
424
  """
369
425
  kacl_changelog = load_changelog(ctx)
370
426
  kacl_config = kacl_changelog.config
427
+ raise_on_invalid(kacl_changelog)
371
428
 
372
- if not kacl_changelog.is_valid():
373
- click.echo(
374
- click.style("Error: ", fg="red")
375
- + "Changelog is not valid. Run 'kacl-cli verify' for more information."
376
- )
377
- sys.exit(1)
378
-
379
- # check if the version string indicates automatic increment
380
- increment = None
381
- if version in ["major", "minor", "patch", "post"]:
382
- increment = version
383
- version = None # reset
384
- else:
385
- # check if version is a valid semantic version
386
- try:
387
- semver.VersionInfo.parse(version)
388
- except Exception:
389
- click.echo(
390
- click.style("Error: ", fg="red")
391
- + f'"{version}" not a valid semantic version.'
392
- )
393
- sys.exit(1)
429
+ version, increment = check_auto_increment(version)
394
430
 
395
- if not kacl_changelog.has_changes():
431
+ if not allow_no_changes and not kacl_changelog.has_changes():
396
432
  click.echo(
397
433
  click.style("Error: ", fg="red")
398
434
  + "The current changelog has no changes. You can only release if changes are available."
@@ -407,7 +443,11 @@ def release(
407
443
  auto_link = True
408
444
 
409
445
  kacl_changelog.release(
410
- version=version, link=link, auto_link=auto_link, increment=increment
446
+ version=version,
447
+ link=link,
448
+ auto_link=auto_link,
449
+ increment=increment,
450
+ allow_no_changes=allow_no_changes,
411
451
  )
412
452
 
413
453
  # get the new version
@@ -511,13 +551,7 @@ def squash(
511
551
  """Squshes all changes from a given version into a single section. Use '--modify' to directly modify the changelog file."""
512
552
 
513
553
  kacl_changelog = load_changelog(ctx)
514
-
515
- if not kacl_changelog.is_valid():
516
- click.echo(
517
- click.style("Error: ", fg="red")
518
- + "Changelog is not valid. Run 'kacl-cli verify' for more information."
519
- )
520
- sys.exit(1)
554
+ raise_on_invalid(kacl_changelog)
521
555
 
522
556
  if not to_version:
523
557
  to_version = kacl_changelog.current_version()
kacl/version.py CHANGED
@@ -215,7 +215,7 @@ class KACLVersion(KACLElement):
215
215
  KACLValidationError(
216
216
  line=element.raw(),
217
217
  line_number=element.line_number(),
218
- error_message=f'"{title}" is not a valid section for a version. Options are [{",".join( config.allowed_version_sections)}]',
218
+ error_message=f'"{title}" is not a valid section for a version. Options are [{",".join(config.allowed_version_sections)}]',
219
219
  start_character_pos=start_pos,
220
220
  end_character_pos=end_pos,
221
221
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-kacl
3
- Version: 0.4.6
3
+ Version: 0.5.0
4
4
  Summary: Python module and CLI tool for validating and modifying Changelogs in "keep-a-changelog" format"
5
5
  Home-page: https://gitlab.com/schmieder.matthias/python-kacl.git
6
6
  Author: Matthias Schmieder
@@ -441,13 +441,12 @@ in order to generate the correct urls, `python-kacl` allows you to define three
441
441
 
442
442
  ```yaml
443
443
  kacl:
444
- git:
445
- links:
446
- # The host url is optional and will be automatically determined using your the git repository. If run on gitlab CI the host will be determined by CI_PROJECT_URL if not specified here.
447
- host_url: https://github.com/mschmieder/kacl-cli
448
- compare_versions_template: '{host}/compare/v{previous_version}...v{version}'
449
- unreleased_changes_template: '{host}/tree/v{latest_version}...HEAD'
450
- initial_version_template: '{host}/tree/v{version}'
444
+ links:
445
+ # The host url is optional and will be automatically determined using your git repository. If run on gitlab CI the host will be determined by CI_PROJECT_URL if not specified here.
446
+ host_url: https://github.com/mschmieder/kacl-cli
447
+ compare_versions_template: '{host}/compare/v{previous_version}...v{version}'
448
+ unreleased_changes_template: '{host}/compare/v{latest_version}...HEAD'
449
+ initial_version_template: '{host}/tree/v{version}'
451
450
  ```
452
451
 
453
452
  Using the python format syntax you can generate any links you want. The available replacement variables are `version`, `previous_version`, `host` and `latest_version`.
@@ -554,7 +553,7 @@ kacl:
554
553
  tag_name: "v{new_version}"
555
554
  tag_description: "Version v{new_version} released"
556
555
  links:
557
- # The host url is optional and will be automatically determined using your the git repository
556
+ # The host url is optional and will be automatically determined using your git repository
558
557
  # host_url: https://github.com/mschmieder/kacl-cli
559
558
  compare_versions_template: '{host}/compare/{previous_version}...{version}'
560
559
  unreleased_changes_template: '{host}/compare/{latest_version}...master'
@@ -581,7 +580,7 @@ source ./venv/bin/activate
581
580
  pip install -e .
582
581
 
583
582
  # install development requirements
584
- pip install -r dev-requirements
583
+ pip install -r dev-requirements.txt
585
584
 
586
585
  # run the tests
587
586
  python3 -m pytest --snapshot-update --allow-snapshot-deletion
@@ -1,24 +1,24 @@
1
- kacl/__init__.py,sha256=euLhodK6bO3OJl7a95-XnJeSdAZ11RppzjoIh_Yc09Q,644
1
+ kacl/__init__.py,sha256=pYb8SW2muL956Qm4dQTELllN-_pox-cDChe6cyIULag,644
2
2
  kacl/__main__.py,sha256=cpulIGoFUJNN5ZW9oUoNN1PnBvrOyJ24xwtpf_yRwgQ,91
3
3
  kacl/changes.py,sha256=A0POz-xFMpFuq7nISnx_K75xOZOMmBuHq6EZ8NOQDsU,661
4
4
  kacl/config.py,sha256=xFWFzsPay09tBmlCClO9FrXpE7IVA0--Zc2ty8Lcvfs,2792
5
- kacl/document.py,sha256=x0AQvWL8zP2uGvWSAtPsSr3O5pKWYqOwZ28PV-61-GU,20447
5
+ kacl/document.py,sha256=zs4_WuXgNcR6zukY1Agr46nqJXqrLu4gBCJDxNU9jUc,21282
6
6
  kacl/element.py,sha256=OG02QI8ND5fJpBMceT6dbIHr4uX9gLxK_fPCsOvMDHI,416
7
7
  kacl/exception.py,sha256=MfOVpUvhcQtm10rSY__74-oLo445PG8mLFGJPPIfXH0,41
8
- kacl/kacl_cli.py,sha256=lJhbLPg8JorEiKNlsWT4wsfUKyNXkaeYe7Jtzo57eLk,16276
8
+ kacl/kacl_cli.py,sha256=Vh6Y6fG0ATCII_GbkzDZDGAgePQYCBk6q3huVsTb828,17161
9
9
  kacl/link_provider.py,sha256=uZ5rpZI9BWUo5e0fw_TsOb9mgvpCWDuJu72LxrW-T5g,1654
10
10
  kacl/parser.py,sha256=kCLMLtVnaynzuBSkQT-qShNwqUmleBuGuMS6nECrlyU,2338
11
11
  kacl/serializer.py,sha256=nxEIpFyNIyH1nGa8Cr3RD6h6a2n9voYOOou-FMPMoJA,2279
12
12
  kacl/validation.py,sha256=b9r_5CyY6Cmi9BePh47IgZ3j64zdVsoB_fFvucieNbw,2125
13
- kacl/version.py,sha256=3UYpOzWqks20uXF9BUcjxo5P979MPiVyzlN1qOqV3mU,9533
13
+ kacl/version.py,sha256=OooYgbCgH6p0fYPOlU_V-I1pwnCVgLSZ2uOg2gjaogw,9532
14
14
  kacl/config/kacl-default.yml,sha256=Dr7rqgDovViIkA1ueIGHMWMjtTry4IKkJI5P-0wHeEg,991
15
15
  tests/__init__.py,sha256=dwb12vsojIwrtIgqwjc2fWhEVgVoiedUaVpZ7kusfY0,23
16
16
  tests/snapshot_directory.py,sha256=yH_ZpzJG5nA6F1ZxCrFPpbxBBON6bxSYO5OYZqKuzZA,1264
17
- tests/test_cli.py,sha256=DuE1fBTVAUkkXsVBCBg5J4L8mOowPSHdhlTtMfwo8Gs,7349
18
- tests/test_kacl.py,sha256=H9H7e0Zy4qGaIOnpwRSv6FFjDGG_A_vuJBhgQ6tTIjc,12545
19
- python_kacl-0.4.6.dist-info/LICENSE,sha256=TRSJOIt7LZuMeZXFleJVL_LHczSxqi70HEiTLT-kI8Q,1075
20
- python_kacl-0.4.6.dist-info/METADATA,sha256=EunJAhaWQq76s2Qcqxl-z7JshtAQgQWVi93f2OANdUo,19310
21
- python_kacl-0.4.6.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
22
- python_kacl-0.4.6.dist-info/entry_points.txt,sha256=8oCwuEyBEH-Z1M_xZIUePgfM7HfkYUAwcXdTPdrnwt4,49
23
- python_kacl-0.4.6.dist-info/top_level.txt,sha256=25gO0j-S3j_-DCptjHqVBlJi3tH_tiN8qyympi1E_Ys,11
24
- python_kacl-0.4.6.dist-info/RECORD,,
17
+ tests/test_cli.py,sha256=-qSa0CzgUAIO0eF9rnuejSDaslYLre8UBZklrThgL-U,8461
18
+ tests/test_kacl.py,sha256=MoLUX6xvTuts9ozwZjJ75PRuZQDa0rsBWzzePhiWKts,13896
19
+ python_kacl-0.5.0.dist-info/LICENSE,sha256=TRSJOIt7LZuMeZXFleJVL_LHczSxqi70HEiTLT-kI8Q,1075
20
+ python_kacl-0.5.0.dist-info/METADATA,sha256=9ZgA5fa0PT0Sp-GDAFhuJGlmnu_o5qRjS0jUmQiYajw,19280
21
+ python_kacl-0.5.0.dist-info/WHEEL,sha256=y4mX-SOX4fYIkonsAGA5N0Oy-8_gI4FXw5HNI1xqvWg,91
22
+ python_kacl-0.5.0.dist-info/entry_points.txt,sha256=8oCwuEyBEH-Z1M_xZIUePgfM7HfkYUAwcXdTPdrnwt4,49
23
+ python_kacl-0.5.0.dist-info/top_level.txt,sha256=25gO0j-S3j_-DCptjHqVBlJi3tH_tiN8qyympi1E_Ys,11
24
+ python_kacl-0.5.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.40.0)
2
+ Generator: setuptools (70.2.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
tests/test_cli.py CHANGED
@@ -38,6 +38,40 @@ def test_verify_with_json_output():
38
38
  assert validation_result["valid"] is False
39
39
 
40
40
 
41
+ def test_current(tmp_path, snapshot):
42
+ runner = CliRunner()
43
+ resources_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data/")
44
+ changelog_file = os.path.join(resources_dir, "CHANGELOG_with_changes.md")
45
+
46
+ with runner.isolated_filesystem(temp_dir=tmp_path):
47
+ result = runner.invoke(
48
+ cli,
49
+ [
50
+ "-f",
51
+ changelog_file,
52
+ "current",
53
+ ],
54
+ catch_exceptions=False,
55
+ )
56
+ assert result.exit_code == 0, result.output
57
+ assert result.output == "1.0.0\n"
58
+
59
+
60
+ def test_next(tmp_path, snapshot):
61
+ runner = CliRunner()
62
+ resources_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data/")
63
+ changelog_file = os.path.join(resources_dir, "CHANGELOG_with_changes.md")
64
+
65
+ with runner.isolated_filesystem(temp_dir=tmp_path):
66
+ result = runner.invoke(
67
+ cli,
68
+ ["-f", changelog_file, "next", "patch"],
69
+ catch_exceptions=False,
70
+ )
71
+ assert result.exit_code == 0, result.output
72
+ assert result.output == "1.0.1\n"
73
+
74
+
41
75
  @freeze_time("2023-01-01")
42
76
  def test_release_patch(tmp_path, snapshot):
43
77
  runner = CliRunner()
tests/test_kacl.py CHANGED
@@ -216,6 +216,17 @@ class TestKacl(TestCase):
216
216
  "https://gitlab.com/schmieder.matthias/python-kacl.git/-/compare/v1.0.0...HEAD",
217
217
  )
218
218
 
219
+ def test_release_with_allow_no_changes_flag(self):
220
+ changelog_file = os.path.join(
221
+ os.path.dirname(os.path.realpath(__file__)),
222
+ "data/CHANGELOG_without_changes.md",
223
+ )
224
+ changelog = kacl.load(changelog_file)
225
+
226
+ self.assertFalse(changelog.has_changes())
227
+ changelog.release("1.0.1", allow_no_changes=True)
228
+ self.assertEqual("1.0.1", changelog.current_version())
229
+
219
230
  def test_release_with_increment(self):
220
231
  tests = {
221
232
  "major": "2.0.0",
@@ -240,6 +251,7 @@ class TestKacl(TestCase):
240
251
 
241
252
  for increment, expected_version in fail_tests.items():
242
253
  changelog = kacl.load(changelog_file)
254
+ changelog.config.post_release_version_prefix = None
243
255
 
244
256
  msg = "This is my first added change"
245
257
  changelog.add("Added", msg)
@@ -269,6 +281,28 @@ class TestKacl(TestCase):
269
281
  changelog.release(increment=increment)
270
282
  self.assertEqual(expected_version, changelog.current_version())
271
283
 
284
+ def test_release_with_increment_extension_hotfix(self):
285
+ tests = {
286
+ "major": "2.0.0",
287
+ "minor": "1.1.0",
288
+ "patch": "1.0.1",
289
+ "post": "1.0.0-hotfix.1",
290
+ }
291
+ changelog_file = os.path.join(
292
+ os.path.dirname(os.path.realpath(__file__)), "data/CHANGELOG.md"
293
+ )
294
+
295
+ for increment, expected_version in tests.items():
296
+ changelog = kacl.load(changelog_file)
297
+ changelog.config.post_release_version_prefix = "hotfix"
298
+
299
+ msg = "This is my first added change"
300
+ changelog.add("Added", msg)
301
+
302
+ self.assertTrue(changelog.has_changes())
303
+ changelog.release(increment=increment)
304
+ self.assertEqual(expected_version, changelog.current_version())
305
+
272
306
  def test_post_release_with_increment(self):
273
307
  tests = {
274
308
  "major": "2.0.0",
@@ -282,6 +316,7 @@ class TestKacl(TestCase):
282
316
 
283
317
  for increment, expected_version in tests.items():
284
318
  changelog = kacl.load(changelog_file)
319
+ changelog.config.post_release_version_prefix = "post"
285
320
 
286
321
  msg = "This is my first added change"
287
322
  changelog.add("Added", msg)