python-kacl 0.5.1__tar.gz → 0.6.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.
Files changed (33) hide show
  1. {python_kacl-0.5.1/python_kacl.egg-info → python_kacl-0.6.0}/PKG-INFO +134 -3
  2. {python_kacl-0.5.1 → python_kacl-0.6.0}/README.md +130 -0
  3. {python_kacl-0.5.1 → python_kacl-0.6.0}/kacl/__init__.py +1 -1
  4. {python_kacl-0.5.1 → python_kacl-0.6.0}/kacl/config/kacl-default.yml +20 -1
  5. {python_kacl-0.5.1 → python_kacl-0.6.0}/kacl/config.py +28 -0
  6. {python_kacl-0.5.1 → python_kacl-0.6.0}/kacl/document.py +73 -1
  7. python_kacl-0.6.0/kacl/jira_client.py +36 -0
  8. {python_kacl-0.5.1 → python_kacl-0.6.0}/kacl/kacl_cli.py +97 -0
  9. {python_kacl-0.5.1 → python_kacl-0.6.0}/kacl/link_provider.py +5 -0
  10. python_kacl-0.6.0/kacl/utils.py +71 -0
  11. {python_kacl-0.5.1 → python_kacl-0.6.0/python_kacl.egg-info}/PKG-INFO +134 -3
  12. {python_kacl-0.5.1 → python_kacl-0.6.0}/python_kacl.egg-info/SOURCES.txt +2 -0
  13. {python_kacl-0.5.1 → python_kacl-0.6.0}/python_kacl.egg-info/requires.txt +1 -0
  14. {python_kacl-0.5.1 → python_kacl-0.6.0}/setup.py +3 -3
  15. {python_kacl-0.5.1 → python_kacl-0.6.0}/tests/test_cli.py +41 -0
  16. {python_kacl-0.5.1 → python_kacl-0.6.0}/tests/test_kacl.py +38 -0
  17. {python_kacl-0.5.1 → python_kacl-0.6.0}/LICENSE +0 -0
  18. {python_kacl-0.5.1 → python_kacl-0.6.0}/MANIFEST.in +0 -0
  19. {python_kacl-0.5.1 → python_kacl-0.6.0}/kacl/__main__.py +0 -0
  20. {python_kacl-0.5.1 → python_kacl-0.6.0}/kacl/changes.py +0 -0
  21. {python_kacl-0.5.1 → python_kacl-0.6.0}/kacl/element.py +0 -0
  22. {python_kacl-0.5.1 → python_kacl-0.6.0}/kacl/exception.py +0 -0
  23. {python_kacl-0.5.1 → python_kacl-0.6.0}/kacl/parser.py +0 -0
  24. {python_kacl-0.5.1 → python_kacl-0.6.0}/kacl/serializer.py +0 -0
  25. {python_kacl-0.5.1 → python_kacl-0.6.0}/kacl/validation.py +0 -0
  26. {python_kacl-0.5.1 → python_kacl-0.6.0}/kacl/version.py +0 -0
  27. {python_kacl-0.5.1 → python_kacl-0.6.0}/python_kacl.egg-info/dependency_links.txt +0 -0
  28. {python_kacl-0.5.1 → python_kacl-0.6.0}/python_kacl.egg-info/entry_points.txt +0 -0
  29. {python_kacl-0.5.1 → python_kacl-0.6.0}/python_kacl.egg-info/not-zip-safe +0 -0
  30. {python_kacl-0.5.1 → python_kacl-0.6.0}/python_kacl.egg-info/top_level.txt +0 -0
  31. {python_kacl-0.5.1 → python_kacl-0.6.0}/setup.cfg +0 -0
  32. {python_kacl-0.5.1 → python_kacl-0.6.0}/tests/__init__.py +0 -0
  33. {python_kacl-0.5.1 → python_kacl-0.6.0}/tests/snapshot_directory.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-kacl
3
- Version: 0.5.1
3
+ Version: 0.6.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
@@ -8,16 +8,17 @@ Author-email: schmieder.matthias@gmail.com
8
8
  License: MIT
9
9
  Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.7
11
+ Classifier: Programming Language :: Python :: 3.9
12
12
  Classifier: Intended Audience :: Developers
13
13
  Classifier: Topic :: Software Development :: Version Control
14
- Requires-Python: >=3.7
14
+ Requires-Python: >=3.9
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
17
  Requires-Dist: click
18
18
  Requires-Dist: semver
19
19
  Requires-Dist: gitpython
20
20
  Requires-Dist: pyyaml
21
+ Requires-Dist: jira
21
22
 
22
23
  # python-kacl
23
24
 
@@ -44,6 +45,10 @@ A tool for verifying and modifying changelog in the [**K**eep-**A-C**hange-**L**
44
45
  - [Link Generation](#link-generation)
45
46
  - [Squashing releases](#squashing-releases)
46
47
  - [Example](#example)
48
+ - [Issue Management Integration](#issue-management-integration)
49
+ - [Adding Comments to Issues](#adding-comments-to-issues)
50
+ - [Command Options](#command-options)
51
+ - [Templating the Comment](#templating-the-comment)
47
52
  - [Extensions](#extensions)
48
53
  - [Post-release/Hotfix](#post-releasehotfix)
49
54
  - [Config file](#config-file)
@@ -483,6 +488,114 @@ kacl-cli -f CHANGELOG.md squash \
483
488
 
484
489
  This example will squash all versions between `0.0.1` and `1.0.0` and move them under `1.0.0`
485
490
 
491
+ ## Issue Management Integration
492
+
493
+ With `python-kacl >= 0.6.0`, you can integrate changelog and release management directly into your Issue Management System. Currently, the supported system is JIRA.
494
+
495
+ It is common practice to reference fixed or addressed issues in the changelog, as shown below:
496
+
497
+ ```markdown
498
+ ## Unreleased
499
+ ### Added
500
+ - JIRA-1754 Just unreleased stuff
501
+
502
+ ## 1.0.0 - 2017-06-20
503
+ ### Added
504
+ - JIRA-9: added UI functionality
505
+
506
+ ### Fixed
507
+ - issue JIRA-13 closed by applying the solution
508
+ ```
509
+
510
+ ### Adding Comments to Issues
511
+
512
+ The `add-comments` command allows you to detect issue IDs using a custom pattern and create comments in the Issue Tracking system about a new release.
513
+
514
+ You can pass all necessary arguments via CLI or set up your `.kacl.yaml` configuration file. The following options are available:
515
+
516
+ ```yaml
517
+ issue_tracker:
518
+ jira:
519
+ host: jira.atlassian.com # The host name of your JIRA instance
520
+ username: null # The username to login. Uses JIRA_USERNAME environment variable if available
521
+ password: null # The password to login. Uses JIRA_PASSWORD environment variable if available
522
+ issue_patterns: # List of regex patterns to identify the issues
523
+ - "[A-Z]+-[0-9]+"
524
+ - "JIRA-[0-9]+"
525
+ comment_template: | # A text template used to comment on the issue
526
+ # 🚀 New version [v{new_version}]({link})
527
+
528
+ A new release has been created referencing this issue. Please check it out.
529
+
530
+ ## 🚧 Changes in this version
531
+
532
+ {changes}
533
+
534
+ ## 🧭 Reference
535
+
536
+ Code: [Source Code Management System]({link})
537
+ ```
538
+
539
+ Run the following command to create the comments:
540
+
541
+ ```bash
542
+ kacl-cli add-comments 1.0.0
543
+ ```
544
+
545
+ ### Command Options
546
+
547
+ See all available command options:
548
+
549
+ ```bash
550
+ Usage: kacl-cli add-comments [OPTIONS] VERSION
551
+
552
+ Adds comments to issues identified within the CHANGELOG. Currently
553
+ supported system: JIRA
554
+
555
+ Options:
556
+ --jira-username TEXT JIRA username. Will also look for the
557
+ JIRA_USERNAME environment variable.
558
+ --jira-password TEXT JIRA password. Will also look for the
559
+ JIRA_PASSWORD environment variable.
560
+ --jira-host TEXT JIRA host. Will also look for the JIRA_HOST
561
+ environment variable.
562
+ --jira-issue-pattern TEXT Issue pattern to search the changelog for. Can
563
+ be specified multiple times.
564
+ --jira-comment-template TEXT JIRA comment template.
565
+ --fail Fail if comments could not be added.
566
+ --help Show this message and exit.
567
+ ```
568
+
569
+ ### Templating the Comment
570
+
571
+ The `comment_template` parameter allows various templating options. The following template variables are available:
572
+
573
+ | Variable | Description |
574
+ | -------------- | ---------------------------------------------------------------------------------------------------- |
575
+ | `new_version` | The version that was just released |
576
+ | `changes` | The markdown content within the change section of your CHANGELOG |
577
+ | `link` | The link to the version within your source code management system according to the link configuration |
578
+ | `env.MYENVVAR` | Any environment variable |
579
+
580
+ This flexibility allows you to adapt the comment patterns to your needs and dynamically create them. For example, adding CI/CD information can be easily achieved as follows:
581
+
582
+ ```yaml
583
+ comment_template: | # A text template used to comment on the issue
584
+ # 🚀 New version [v{new_version}]({link})
585
+
586
+ A new release has been created referencing this issue. Please check it out.
587
+
588
+ ## 🚧 Changes in this version
589
+
590
+ {changes}
591
+
592
+ ## 🧭 Reference
593
+
594
+ Code: [Source Code Management System]({link})
595
+ Pipeline: [Pipeline ({env.CI_PIPELINE_IID})]({env.CI_PIPELINE_URL})
596
+ GitLab Project: [({env.CI_PROJECT_TITLE})]({env.CI_PROJECT_URL})
597
+ ```
598
+
486
599
  ## Extensions
487
600
 
488
601
  ### Post-release/Hotfix
@@ -561,6 +674,24 @@ kacl:
561
674
  auto_generate: True
562
675
  extension:
563
676
  post_release_version_prefix: null
677
+ issue_tracker:
678
+ jira:
679
+ host: null
680
+ username: null
681
+ password: null
682
+ issue_patterns: ["[A-Z]+-[0-9]+"]
683
+ comment_template: |
684
+ # 🚀 New version [v{new_version}]({link})
685
+
686
+ A new release has been created referencing this issue. Please check it out.
687
+
688
+ ## 🚧 Changes in this version
689
+
690
+ {changes}
691
+
692
+ ## 🧭 Reference
693
+
694
+ Code: [Source Code Management System]({link})
564
695
  ```
565
696
 
566
697
  ## Development
@@ -23,6 +23,10 @@ A tool for verifying and modifying changelog in the [**K**eep-**A-C**hange-**L**
23
23
  - [Link Generation](#link-generation)
24
24
  - [Squashing releases](#squashing-releases)
25
25
  - [Example](#example)
26
+ - [Issue Management Integration](#issue-management-integration)
27
+ - [Adding Comments to Issues](#adding-comments-to-issues)
28
+ - [Command Options](#command-options)
29
+ - [Templating the Comment](#templating-the-comment)
26
30
  - [Extensions](#extensions)
27
31
  - [Post-release/Hotfix](#post-releasehotfix)
28
32
  - [Config file](#config-file)
@@ -462,6 +466,114 @@ kacl-cli -f CHANGELOG.md squash \
462
466
 
463
467
  This example will squash all versions between `0.0.1` and `1.0.0` and move them under `1.0.0`
464
468
 
469
+ ## Issue Management Integration
470
+
471
+ With `python-kacl >= 0.6.0`, you can integrate changelog and release management directly into your Issue Management System. Currently, the supported system is JIRA.
472
+
473
+ It is common practice to reference fixed or addressed issues in the changelog, as shown below:
474
+
475
+ ```markdown
476
+ ## Unreleased
477
+ ### Added
478
+ - JIRA-1754 Just unreleased stuff
479
+
480
+ ## 1.0.0 - 2017-06-20
481
+ ### Added
482
+ - JIRA-9: added UI functionality
483
+
484
+ ### Fixed
485
+ - issue JIRA-13 closed by applying the solution
486
+ ```
487
+
488
+ ### Adding Comments to Issues
489
+
490
+ The `add-comments` command allows you to detect issue IDs using a custom pattern and create comments in the Issue Tracking system about a new release.
491
+
492
+ You can pass all necessary arguments via CLI or set up your `.kacl.yaml` configuration file. The following options are available:
493
+
494
+ ```yaml
495
+ issue_tracker:
496
+ jira:
497
+ host: jira.atlassian.com # The host name of your JIRA instance
498
+ username: null # The username to login. Uses JIRA_USERNAME environment variable if available
499
+ password: null # The password to login. Uses JIRA_PASSWORD environment variable if available
500
+ issue_patterns: # List of regex patterns to identify the issues
501
+ - "[A-Z]+-[0-9]+"
502
+ - "JIRA-[0-9]+"
503
+ comment_template: | # A text template used to comment on the issue
504
+ # 🚀 New version [v{new_version}]({link})
505
+
506
+ A new release has been created referencing this issue. Please check it out.
507
+
508
+ ## 🚧 Changes in this version
509
+
510
+ {changes}
511
+
512
+ ## 🧭 Reference
513
+
514
+ Code: [Source Code Management System]({link})
515
+ ```
516
+
517
+ Run the following command to create the comments:
518
+
519
+ ```bash
520
+ kacl-cli add-comments 1.0.0
521
+ ```
522
+
523
+ ### Command Options
524
+
525
+ See all available command options:
526
+
527
+ ```bash
528
+ Usage: kacl-cli add-comments [OPTIONS] VERSION
529
+
530
+ Adds comments to issues identified within the CHANGELOG. Currently
531
+ supported system: JIRA
532
+
533
+ Options:
534
+ --jira-username TEXT JIRA username. Will also look for the
535
+ JIRA_USERNAME environment variable.
536
+ --jira-password TEXT JIRA password. Will also look for the
537
+ JIRA_PASSWORD environment variable.
538
+ --jira-host TEXT JIRA host. Will also look for the JIRA_HOST
539
+ environment variable.
540
+ --jira-issue-pattern TEXT Issue pattern to search the changelog for. Can
541
+ be specified multiple times.
542
+ --jira-comment-template TEXT JIRA comment template.
543
+ --fail Fail if comments could not be added.
544
+ --help Show this message and exit.
545
+ ```
546
+
547
+ ### Templating the Comment
548
+
549
+ The `comment_template` parameter allows various templating options. The following template variables are available:
550
+
551
+ | Variable | Description |
552
+ | -------------- | ---------------------------------------------------------------------------------------------------- |
553
+ | `new_version` | The version that was just released |
554
+ | `changes` | The markdown content within the change section of your CHANGELOG |
555
+ | `link` | The link to the version within your source code management system according to the link configuration |
556
+ | `env.MYENVVAR` | Any environment variable |
557
+
558
+ This flexibility allows you to adapt the comment patterns to your needs and dynamically create them. For example, adding CI/CD information can be easily achieved as follows:
559
+
560
+ ```yaml
561
+ comment_template: | # A text template used to comment on the issue
562
+ # 🚀 New version [v{new_version}]({link})
563
+
564
+ A new release has been created referencing this issue. Please check it out.
565
+
566
+ ## 🚧 Changes in this version
567
+
568
+ {changes}
569
+
570
+ ## 🧭 Reference
571
+
572
+ Code: [Source Code Management System]({link})
573
+ Pipeline: [Pipeline ({env.CI_PIPELINE_IID})]({env.CI_PIPELINE_URL})
574
+ GitLab Project: [({env.CI_PROJECT_TITLE})]({env.CI_PROJECT_URL})
575
+ ```
576
+
465
577
  ## Extensions
466
578
 
467
579
  ### Post-release/Hotfix
@@ -540,6 +652,24 @@ kacl:
540
652
  auto_generate: True
541
653
  extension:
542
654
  post_release_version_prefix: null
655
+ issue_tracker:
656
+ jira:
657
+ host: null
658
+ username: null
659
+ password: null
660
+ issue_patterns: ["[A-Z]+-[0-9]+"]
661
+ comment_template: |
662
+ # 🚀 New version [v{new_version}]({link})
663
+
664
+ A new release has been created referencing this issue. Please check it out.
665
+
666
+ ## 🚧 Changes in this version
667
+
668
+ {changes}
669
+
670
+ ## 🧭 Reference
671
+
672
+ Code: [Source Code Management System]({link})
543
673
  ```
544
674
 
545
675
  ## Development
@@ -1,5 +1,5 @@
1
1
  # Version of the python-kacl package
2
- __version__ = "0.5.1"
2
+ __version__ = "0.6.0"
3
3
 
4
4
  from kacl.document import KACLDocument
5
5
  from kacl.serializer import KACLMarkdownSerializer
@@ -26,4 +26,23 @@ kacl:
26
26
  unreleased_changes_template: '{host}/compare/{latest_version}...master'
27
27
  initial_version_template: '{host}/tree/{version}'
28
28
  extension:
29
- post_release_version_prefix: null
29
+ post_release_version_prefix: null
30
+ issue_tracker:
31
+ jira:
32
+ host: null
33
+ username: null
34
+ password: null
35
+ issue_patterns: ["[A-Z]+-[0-9]+"]
36
+ comment_template: |
37
+ # 🚀 New version [v{new_version}]({link})
38
+
39
+ A new release has been created referencing this issue. Please check it out.
40
+
41
+ ## 🚧 Changes in this version
42
+
43
+ {changes}
44
+
45
+ ## 🧭 Reference
46
+
47
+ Code: [Source Code Management System]({link})
48
+
@@ -44,6 +44,34 @@ class KACLConfig:
44
44
  "post_release_version_prefix"
45
45
  )
46
46
 
47
+ # issue tracker config
48
+ self.issue_tracker_jira_host = (
49
+ self.__config.get("issue_tracker", {}).get("jira", {}).get("host")
50
+ )
51
+ self.issue_tracker_jira_username = (
52
+ self.__config.get("issue_tracker", {}).get("jira", {}).get("username")
53
+ )
54
+ self.issue_tracker_jira_password = (
55
+ self.__config.get("issue_tracker", {}).get("jira", {}).get("password")
56
+ )
57
+ self.issue_tracker_jira_issue_patterns = (
58
+ self.__config.get("issue_tracker", {})
59
+ .get("jira", {})
60
+ .get("issue_patterns", [])
61
+ )
62
+ self.issue_tracker_jira_comment_template = (
63
+ self.__config.get("issue_tracker", {})
64
+ .get("jira", {})
65
+ .get("comment_template")
66
+ )
67
+
68
+ if not self.issue_tracker_jira_host:
69
+ self.issue_tracker_jira_host = os.getenv("JIRA_HOST", None)
70
+ if not self.issue_tracker_jira_username:
71
+ self.issue_tracker_jira_username = os.getenv("JIRA_USERNAME", None)
72
+ if not self.issue_tracker_jira_password:
73
+ self.issue_tracker_jira_password = os.getenv("JIRA_PASSWORD", None)
74
+
47
75
  @staticmethod
48
76
  def merge(a, b, path=None):
49
77
  """merge two dictionaries
@@ -7,8 +7,12 @@ import semver
7
7
  from kacl.config import KACLConfig
8
8
  from kacl.element import KACLElement
9
9
  from kacl.exception import KACLException
10
+ from kacl.jira_client import KACLJiraClient
10
11
  from kacl.link_provider import LinkProvider
11
12
  from kacl.parser import KACLParser
13
+ from kacl.utils import convert_markdown_to_jira
14
+ from kacl.utils import extract_issue_ids
15
+ from kacl.utils import replace_env_variables
12
16
  from kacl.validation import KACLValidation
13
17
  from kacl.version import KACLVersion
14
18
 
@@ -361,7 +365,7 @@ class KACLDocument:
361
365
  link_provider.initial_version(**fargs)
362
366
  )
363
367
 
364
- def get(self, version):
368
+ def get(self, version) -> KACLVersion:
365
369
  """Returns the selected version
366
370
 
367
371
  Arguments:
@@ -462,6 +466,74 @@ class KACLDocument:
462
466
  else:
463
467
  versions[0].set_link(link_provider.initial_version(**fargs))
464
468
 
469
+ def get_associated_issues(self, version=None):
470
+ """returns all issues for the given version"""
471
+ kacl_version = None
472
+ if not version:
473
+ # if no version is given, we will use the current version
474
+ kacl_version = self.get(self.current_version())
475
+ else:
476
+ kacl_version = self.get(version)
477
+
478
+ patterns = {"jira": self.config.issue_tracker_jira_issue_patterns}
479
+
480
+ # get all issue ids
481
+ return extract_issue_ids(kacl_version, patterns)
482
+
483
+ def render_comments(self, version=None) -> dict[str:str]:
484
+ if version is None:
485
+ version = self.current_version()
486
+
487
+ changes = self.get(version)
488
+ link_provider = self.__get_link_provider()
489
+
490
+ format_string = {
491
+ "new_version": version,
492
+ "changes": changes.body(),
493
+ "link": link_provider.version_link(version),
494
+ }
495
+
496
+ comments = {}
497
+
498
+ if self.config.issue_tracker_jira_comment_template:
499
+ jira_comment = replace_env_variables(
500
+ self.config.issue_tracker_jira_comment_template
501
+ )
502
+ jira_comment = jira_comment.format(**format_string)
503
+ jira_comment = convert_markdown_to_jira(jira_comment)
504
+ comments["jira"] = jira_comment
505
+
506
+ return comments
507
+
508
+ def add_comments(self, version=None) -> dict[str : dict[str:bool]]:
509
+ # This function will parse all versions for known issue ids and search the defined
510
+ # issue tracking system for the issue. If the issue is found, a comment will be added to the
511
+ # issue with the changelog/release information
512
+
513
+ issue_ids = self.get_associated_issues(version)
514
+
515
+ jira_client = None
516
+ if "jira" in issue_ids and len(issue_ids["jira"]) > 0:
517
+ client = KACLJiraClient(self.config)
518
+ if client.is_authenticated():
519
+ jira_client = client
520
+
521
+ rendered_comments = self.render_comments(version)
522
+
523
+ report = {"jira": {}}
524
+
525
+ if jira_client:
526
+ # comment on jira tickets
527
+ for jira_issue in issue_ids["jira"]:
528
+ try:
529
+ jira_client.add_comment(jira_issue, rendered_comments["jira"])
530
+ report["jira"][jira_issue] = True
531
+ except Exception:
532
+ report["jira"][jira_issue] = False
533
+ continue
534
+
535
+ return report
536
+
465
537
  def header(self):
466
538
  """Gives access to the top level heading element
467
539
 
@@ -0,0 +1,36 @@
1
+ from jira import JIRA
2
+
3
+ from kacl.config import KACLConfig
4
+
5
+
6
+ class KACLJiraClient:
7
+ def __init__(self, config: KACLConfig):
8
+ self.config = config
9
+ self.client = JIRA(
10
+ server=config.issue_tracker_jira_host,
11
+ basic_auth=(
12
+ config.issue_tracker_jira_username,
13
+ config.issue_tracker_jira_password,
14
+ ),
15
+ )
16
+
17
+ def is_authenticated(self):
18
+ try:
19
+ # Attempt to fetch the current user as a way to check if authentication was successful
20
+ self.client.current_user()
21
+ except Exception:
22
+ return False
23
+ return True
24
+
25
+ def add_comment(self, issue_key, comment):
26
+ """
27
+ Adds a comment to a JIRA issue.
28
+
29
+ Args:
30
+ issue_key (str): The key of the issue to add the comment to.
31
+ comment (str): The text of the comment to add.
32
+
33
+ Returns:
34
+ None
35
+ """
36
+ self.client.add_comment(issue_key, comment)
@@ -611,6 +611,103 @@ def new(output_file):
611
611
  click.echo(kacl_changelog_content)
612
612
 
613
613
 
614
+ @cli.command("add-comments")
615
+ @click.pass_context
616
+ @click.option(
617
+ "--jira-username",
618
+ required=False,
619
+ type=str,
620
+ default=os.getenv(
621
+ "JIRA_USERNAME",
622
+ None,
623
+ ),
624
+ help="JIRA username. Will also look for JIRA_USERNAME environment variable.",
625
+ )
626
+ @click.option(
627
+ "--jira-password",
628
+ required=False,
629
+ type=str,
630
+ default=os.getenv(
631
+ "JIRA_PASSWORD",
632
+ None,
633
+ ),
634
+ help="JIRA password. Will also look for JIRA_PASSWORD environment variable.",
635
+ )
636
+ @click.option(
637
+ "--jira-host",
638
+ required=False,
639
+ type=str,
640
+ default=os.getenv(
641
+ "JIRA_HOST",
642
+ None,
643
+ ),
644
+ help="JIRA host. Will also look for JIRA_HOST environment variable.",
645
+ )
646
+ @click.option(
647
+ "--jira-issue-pattern",
648
+ required=False,
649
+ multiple=True,
650
+ help="Issue pattern to search the changlog for. Can be specified multiple times.",
651
+ )
652
+ @click.option(
653
+ "--jira-comment-template",
654
+ required=False,
655
+ default=None,
656
+ type=str,
657
+ help="JIRA comment template.",
658
+ )
659
+ @click.option(
660
+ "--fail",
661
+ is_flag=True,
662
+ help="Will fail if comments could not be added.",
663
+ )
664
+ @click.argument("version", type=str)
665
+ def add_comments(
666
+ ctx,
667
+ version,
668
+ jira_username,
669
+ jira_password,
670
+ jira_host,
671
+ jira_issue_pattern,
672
+ jira_comment_template,
673
+ fail,
674
+ ):
675
+ """Will add comments to issues identified within the CHANGELOG. Currently supported systems are: [JIRA]"""
676
+ kacl_changelog = load_changelog(ctx)
677
+ kacl_config = kacl_changelog.config
678
+
679
+ if jira_host:
680
+ kacl_config.issue_tracker_jira_host = jira_host
681
+ if jira_username:
682
+ kacl_config.issue_tracker_jira_username = jira_username
683
+ if jira_password:
684
+ kacl_config.issue_tracker_jira_password = jira_password
685
+ if jira_issue_pattern:
686
+ kacl_config.issue_tracker_jira_issue_patterns = list(jira_issue_pattern)
687
+ if jira_comment_template:
688
+ kacl_config.issue_tracker_jira_comment_template = jira_comment_template
689
+
690
+ raise_on_invalid(kacl_changelog)
691
+
692
+ issues = kacl_changelog.get_associated_issues(version)
693
+ for issue_type in issues.keys():
694
+ click.secho(f"Found {len(issues[issue_type])} '{issue_type}' issues", fg="blue")
695
+ for issue in issues[issue_type]:
696
+ click.secho(f"{issue_type}: {issue}")
697
+
698
+ report = kacl_changelog.add_comments(version)
699
+
700
+ failed = False
701
+ for issue_type in report.keys():
702
+ for issue, success in report[issue_type].items():
703
+ if not success:
704
+ failed = True
705
+ click.secho(f"Failed to add comment to {issue_type}: {issue}", fg="red")
706
+
707
+ if fail and failed:
708
+ sys.exit(1)
709
+
710
+
614
711
  def start():
615
712
  try:
616
713
  cli(obj={})
@@ -49,3 +49,8 @@ class LinkProvider:
49
49
  previous_version=previous_version,
50
50
  latest_version=latest_version,
51
51
  )
52
+
53
+ def version_link(self, version=None, previous_version=None, latest_version=None):
54
+ return self.initial_version(
55
+ version=version, previous_version=version, latest_version=version
56
+ )
@@ -0,0 +1,71 @@
1
+ import os
2
+ import re
3
+
4
+ from kacl.version import KACLVersion
5
+
6
+
7
+ def extract_issue_ids(
8
+ version: KACLVersion,
9
+ search_patterns: dict[str : list[str]] = {"jira": [r"[A-Z]+-[0-9]+"]},
10
+ ) -> dict[str : list[str]]:
11
+ """
12
+ Extract JIRA IDs from a version object.
13
+
14
+ Args:
15
+ version (KACLVersion): The version object to extract JIRA IDs from.
16
+ search_patterns (list[str], optional): A list of search patterns to use. Defaults to ["[A-Z]+-[0-9]+"].
17
+
18
+ Returns:
19
+ list[str]: A list of JIRA IDs.
20
+ """
21
+ issue_ids = {}
22
+ for key in search_patterns.keys():
23
+ ids = []
24
+ for pattern in search_patterns[key]:
25
+ ids.extend(re.findall(pattern, (version.body())))
26
+ issue_ids[key] = ids
27
+ return issue_ids
28
+
29
+
30
+ def replace_env_variables(text):
31
+ """
32
+ Parses a given text and replaces occurrences of {env.VAR} with the value of the environment
33
+ variable VAR if it exists, or with an empty string if it does not.
34
+
35
+ Args:
36
+ text (str): The text to parse and replace environment variables in.
37
+
38
+ Returns:
39
+ str: The text with environment variables replaced.
40
+ """
41
+ # Regular expression to find {env.VAR} patterns
42
+ pattern = re.compile(r"\{env\.([A-Za-z_][A-Za-z0-9_]*)\}")
43
+
44
+ # Function to replace each match
45
+ def replace_match(match):
46
+ # Extract the environment variable name from the match
47
+ env_var = match.group(1)
48
+ # Return the environment variable's value if it exists, else return an empty string
49
+ return os.getenv(env_var, "")
50
+
51
+ # Replace all occurrences of the pattern in the text
52
+ return pattern.sub(replace_match, text)
53
+
54
+
55
+ def convert_markdown_to_jira(markdown_text):
56
+ # Convert markdown headings to JIRA headings
57
+ def replace_heading(match):
58
+ heading_level = len(match.group(1))
59
+ return f"h{heading_level}. {match.group(2)}"
60
+
61
+ # Convert markdown links to JIRA links
62
+ def replace_link(match):
63
+ link_text = match.group(1)
64
+ url = match.group(2)
65
+ return f"[{link_text}|{url}]"
66
+
67
+ converted_text = re.sub(
68
+ r"^(#{1,6})\s+(.*)", replace_heading, markdown_text, flags=re.MULTILINE
69
+ )
70
+ converted_text = re.sub(r"\[(.*?)\]\((.*?)\)", replace_link, converted_text)
71
+ return converted_text
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-kacl
3
- Version: 0.5.1
3
+ Version: 0.6.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
@@ -8,16 +8,17 @@ Author-email: schmieder.matthias@gmail.com
8
8
  License: MIT
9
9
  Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.7
11
+ Classifier: Programming Language :: Python :: 3.9
12
12
  Classifier: Intended Audience :: Developers
13
13
  Classifier: Topic :: Software Development :: Version Control
14
- Requires-Python: >=3.7
14
+ Requires-Python: >=3.9
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
17
  Requires-Dist: click
18
18
  Requires-Dist: semver
19
19
  Requires-Dist: gitpython
20
20
  Requires-Dist: pyyaml
21
+ Requires-Dist: jira
21
22
 
22
23
  # python-kacl
23
24
 
@@ -44,6 +45,10 @@ A tool for verifying and modifying changelog in the [**K**eep-**A-C**hange-**L**
44
45
  - [Link Generation](#link-generation)
45
46
  - [Squashing releases](#squashing-releases)
46
47
  - [Example](#example)
48
+ - [Issue Management Integration](#issue-management-integration)
49
+ - [Adding Comments to Issues](#adding-comments-to-issues)
50
+ - [Command Options](#command-options)
51
+ - [Templating the Comment](#templating-the-comment)
47
52
  - [Extensions](#extensions)
48
53
  - [Post-release/Hotfix](#post-releasehotfix)
49
54
  - [Config file](#config-file)
@@ -483,6 +488,114 @@ kacl-cli -f CHANGELOG.md squash \
483
488
 
484
489
  This example will squash all versions between `0.0.1` and `1.0.0` and move them under `1.0.0`
485
490
 
491
+ ## Issue Management Integration
492
+
493
+ With `python-kacl >= 0.6.0`, you can integrate changelog and release management directly into your Issue Management System. Currently, the supported system is JIRA.
494
+
495
+ It is common practice to reference fixed or addressed issues in the changelog, as shown below:
496
+
497
+ ```markdown
498
+ ## Unreleased
499
+ ### Added
500
+ - JIRA-1754 Just unreleased stuff
501
+
502
+ ## 1.0.0 - 2017-06-20
503
+ ### Added
504
+ - JIRA-9: added UI functionality
505
+
506
+ ### Fixed
507
+ - issue JIRA-13 closed by applying the solution
508
+ ```
509
+
510
+ ### Adding Comments to Issues
511
+
512
+ The `add-comments` command allows you to detect issue IDs using a custom pattern and create comments in the Issue Tracking system about a new release.
513
+
514
+ You can pass all necessary arguments via CLI or set up your `.kacl.yaml` configuration file. The following options are available:
515
+
516
+ ```yaml
517
+ issue_tracker:
518
+ jira:
519
+ host: jira.atlassian.com # The host name of your JIRA instance
520
+ username: null # The username to login. Uses JIRA_USERNAME environment variable if available
521
+ password: null # The password to login. Uses JIRA_PASSWORD environment variable if available
522
+ issue_patterns: # List of regex patterns to identify the issues
523
+ - "[A-Z]+-[0-9]+"
524
+ - "JIRA-[0-9]+"
525
+ comment_template: | # A text template used to comment on the issue
526
+ # 🚀 New version [v{new_version}]({link})
527
+
528
+ A new release has been created referencing this issue. Please check it out.
529
+
530
+ ## 🚧 Changes in this version
531
+
532
+ {changes}
533
+
534
+ ## 🧭 Reference
535
+
536
+ Code: [Source Code Management System]({link})
537
+ ```
538
+
539
+ Run the following command to create the comments:
540
+
541
+ ```bash
542
+ kacl-cli add-comments 1.0.0
543
+ ```
544
+
545
+ ### Command Options
546
+
547
+ See all available command options:
548
+
549
+ ```bash
550
+ Usage: kacl-cli add-comments [OPTIONS] VERSION
551
+
552
+ Adds comments to issues identified within the CHANGELOG. Currently
553
+ supported system: JIRA
554
+
555
+ Options:
556
+ --jira-username TEXT JIRA username. Will also look for the
557
+ JIRA_USERNAME environment variable.
558
+ --jira-password TEXT JIRA password. Will also look for the
559
+ JIRA_PASSWORD environment variable.
560
+ --jira-host TEXT JIRA host. Will also look for the JIRA_HOST
561
+ environment variable.
562
+ --jira-issue-pattern TEXT Issue pattern to search the changelog for. Can
563
+ be specified multiple times.
564
+ --jira-comment-template TEXT JIRA comment template.
565
+ --fail Fail if comments could not be added.
566
+ --help Show this message and exit.
567
+ ```
568
+
569
+ ### Templating the Comment
570
+
571
+ The `comment_template` parameter allows various templating options. The following template variables are available:
572
+
573
+ | Variable | Description |
574
+ | -------------- | ---------------------------------------------------------------------------------------------------- |
575
+ | `new_version` | The version that was just released |
576
+ | `changes` | The markdown content within the change section of your CHANGELOG |
577
+ | `link` | The link to the version within your source code management system according to the link configuration |
578
+ | `env.MYENVVAR` | Any environment variable |
579
+
580
+ This flexibility allows you to adapt the comment patterns to your needs and dynamically create them. For example, adding CI/CD information can be easily achieved as follows:
581
+
582
+ ```yaml
583
+ comment_template: | # A text template used to comment on the issue
584
+ # 🚀 New version [v{new_version}]({link})
585
+
586
+ A new release has been created referencing this issue. Please check it out.
587
+
588
+ ## 🚧 Changes in this version
589
+
590
+ {changes}
591
+
592
+ ## 🧭 Reference
593
+
594
+ Code: [Source Code Management System]({link})
595
+ Pipeline: [Pipeline ({env.CI_PIPELINE_IID})]({env.CI_PIPELINE_URL})
596
+ GitLab Project: [({env.CI_PROJECT_TITLE})]({env.CI_PROJECT_URL})
597
+ ```
598
+
486
599
  ## Extensions
487
600
 
488
601
  ### Post-release/Hotfix
@@ -561,6 +674,24 @@ kacl:
561
674
  auto_generate: True
562
675
  extension:
563
676
  post_release_version_prefix: null
677
+ issue_tracker:
678
+ jira:
679
+ host: null
680
+ username: null
681
+ password: null
682
+ issue_patterns: ["[A-Z]+-[0-9]+"]
683
+ comment_template: |
684
+ # 🚀 New version [v{new_version}]({link})
685
+
686
+ A new release has been created referencing this issue. Please check it out.
687
+
688
+ ## 🚧 Changes in this version
689
+
690
+ {changes}
691
+
692
+ ## 🧭 Reference
693
+
694
+ Code: [Source Code Management System]({link})
564
695
  ```
565
696
 
566
697
  ## Development
@@ -9,10 +9,12 @@ kacl/config.py
9
9
  kacl/document.py
10
10
  kacl/element.py
11
11
  kacl/exception.py
12
+ kacl/jira_client.py
12
13
  kacl/kacl_cli.py
13
14
  kacl/link_provider.py
14
15
  kacl/parser.py
15
16
  kacl/serializer.py
17
+ kacl/utils.py
16
18
  kacl/validation.py
17
19
  kacl/version.py
18
20
  kacl/config/kacl-default.yml
@@ -2,3 +2,4 @@ click
2
2
  semver
3
3
  gitpython
4
4
  pyyaml
5
+ jira
@@ -11,7 +11,7 @@ this_directory = path.abspath(path.dirname(__file__))
11
11
  with open(path.join(this_directory, "README.md"), encoding="utf-8") as f:
12
12
  long_description = f.read()
13
13
 
14
- version = "0.5.1"
14
+ version = "0.6.0"
15
15
 
16
16
  version = f"{version}{os.environ.get('PIP_VERSION_POSTFIX', '')}"
17
17
 
@@ -37,13 +37,13 @@ setup(
37
37
  license="MIT",
38
38
  packages=find_packages(),
39
39
  include_package_data=True,
40
- python_requires=">=3.7",
40
+ python_requires=">=3.9",
41
41
  install_requires=requirements,
42
42
  zip_safe=False,
43
43
  classifiers=[
44
44
  "License :: OSI Approved :: MIT License",
45
45
  "Programming Language :: Python :: 3",
46
- "Programming Language :: Python :: 3.7",
46
+ "Programming Language :: Python :: 3.9",
47
47
  "Intended Audience :: Developers",
48
48
  "Topic :: Software Development :: Version Control",
49
49
  ],
@@ -2,6 +2,7 @@ import json
2
2
  import os
3
3
  import shutil
4
4
 
5
+ import pytest
5
6
  from click.testing import CliRunner
6
7
  from freezegun import freeze_time
7
8
 
@@ -375,3 +376,43 @@ def test_squash_current(tmp_path, snapshot):
375
376
  )
376
377
  assert result.exit_code == 0, result.output
377
378
  snapshot_directory(snapshot=snapshot, directory_path=project_root_path)
379
+
380
+
381
+ @pytest.mark.skip(reason="No issue tracker openly available, test locally.")
382
+ def test_add_comments(tmp_path):
383
+ runner = CliRunner()
384
+ root_dir = os.path.join(
385
+ os.path.dirname(os.path.realpath(__file__)),
386
+ "..",
387
+ )
388
+ resources_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data/")
389
+ changelog_file = os.path.join(resources_dir, "CHANGELOG_issue_management.md")
390
+
391
+ with runner.isolated_filesystem(temp_dir=tmp_path) as project_root_path:
392
+ shutil.copyfile(changelog_file, os.path.join(project_root_path, "CHANGELOG.md"))
393
+ shutil.copytree(
394
+ os.path.join(root_dir, ".git"), os.path.join(project_root_path, ".git")
395
+ )
396
+ result = runner.invoke(
397
+ cli,
398
+ [
399
+ "-f",
400
+ "CHANGELOG.md",
401
+ "add-comments",
402
+ "1.0.0",
403
+ "--jira-username",
404
+ os.getenv("JIRA_USERNAME"),
405
+ "--jira-password",
406
+ os.getenv("JIRA_PASSWORD"),
407
+ "--jira-host",
408
+ os.getenv("JIRA_HOST"),
409
+ "--jira-issue-pattern",
410
+ "JIRA-[0-9]+",
411
+ "--jira-issue-pattern",
412
+ "MYJIRA-[0-9]+",
413
+ "--jira-issue-pattern",
414
+ "[A-Z]+-[0-9]+",
415
+ ],
416
+ catch_exceptions=False,
417
+ )
418
+ assert result.exit_code == 0, result.output
@@ -1,6 +1,7 @@
1
1
  import os
2
2
  from unittest import TestCase
3
3
 
4
+ import pytest
4
5
  import yaml
5
6
 
6
7
  import kacl
@@ -406,3 +407,40 @@ def test_squash(tmp_path, snapshot):
406
407
  f.write(kacl.dump(changelog))
407
408
 
408
409
  snapshot_directory(snapshot=snapshot, directory_path=tmp_path)
410
+
411
+
412
+ @pytest.mark.skip(reason="No issue tracker openly available, test locally.")
413
+ def test_issue_tracker_comment(tmp_path):
414
+ changelog_file = os.path.join(
415
+ os.path.dirname(os.path.realpath(__file__)),
416
+ "data/CHANGELOG_issue_management.md",
417
+ )
418
+ changelog = kacl.load(changelog_file)
419
+ assert changelog.is_valid()
420
+
421
+ changelog.add_comments(version=changelog.current_version())
422
+
423
+
424
+ def test_render_comments(tmp_path):
425
+ changelog_file = os.path.join(
426
+ os.path.dirname(os.path.realpath(__file__)),
427
+ "data/CHANGELOG_issue_management.md",
428
+ )
429
+ changelog = kacl.load(changelog_file)
430
+ assert changelog.is_valid()
431
+
432
+ rendered_comments = changelog.render_comments(version=changelog.current_version())
433
+
434
+ assert "jira" in rendered_comments
435
+
436
+
437
+ def test_get_issues(tmp_path):
438
+ changelog_file = os.path.join(
439
+ os.path.dirname(os.path.realpath(__file__)),
440
+ "data/CHANGELOG_issue_management.md",
441
+ )
442
+ changelog = kacl.load(changelog_file)
443
+ assert changelog.is_valid()
444
+
445
+ issues = changelog.get_associated_issues(version=changelog.current_version())
446
+ assert len(issues["jira"]) == 5
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes