python-kacl 0.6.4__py3-none-any.whl → 0.6.6__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 +4 -4
- kacl/changes.py +2 -0
- kacl/config/kacl-default.yml +3 -1
- kacl/config.py +3 -0
- kacl/document.py +112 -7
- kacl/kacl_cli.py +76 -3
- kacl/serializer.py +7 -2
- {python_kacl-0.6.4.dist-info → python_kacl-0.6.6.dist-info}/METADATA +262 -17
- python_kacl-0.6.6.dist-info/RECORD +22 -0
- {python_kacl-0.6.4.dist-info → python_kacl-0.6.6.dist-info}/entry_points.txt +1 -0
- python_kacl-0.6.6.dist-info/top_level.txt +1 -0
- python_kacl-0.6.4.dist-info/RECORD +0 -26
- python_kacl-0.6.4.dist-info/top_level.txt +0 -2
- tests/__init__.py +0 -1
- tests/snapshot_directory.py +0 -42
- tests/test_cli.py +0 -492
- tests/test_kacl.py +0 -479
- {python_kacl-0.6.4.dist-info → python_kacl-0.6.6.dist-info}/WHEEL +0 -0
- {python_kacl-0.6.4.dist-info → python_kacl-0.6.6.dist-info}/licenses/LICENSE +0 -0
kacl/__init__.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Version of the python-kacl package
|
|
2
|
-
__version__ = "0.6.
|
|
2
|
+
__version__ = "0.6.6"
|
|
3
3
|
|
|
4
4
|
from kacl.document import KACLDocument
|
|
5
5
|
from kacl.serializer import KACLMarkdownSerializer
|
|
@@ -14,14 +14,14 @@ def load(file):
|
|
|
14
14
|
with open(file, "r") as f:
|
|
15
15
|
document = f.read()
|
|
16
16
|
try:
|
|
17
|
-
doc = KACLDocument.parse(document)
|
|
17
|
+
doc = KACLDocument.parse(document, file)
|
|
18
18
|
finally:
|
|
19
19
|
f.close()
|
|
20
20
|
return doc
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
def parse(text):
|
|
24
|
-
return KACLDocument.parse(text)
|
|
23
|
+
def parse(text, file=None):
|
|
24
|
+
return KACLDocument.parse(text, file)
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
def dump(document):
|
kacl/changes.py
CHANGED
kacl/config/kacl-default.yml
CHANGED
kacl/config.py
CHANGED
|
@@ -72,6 +72,9 @@ class KACLConfig:
|
|
|
72
72
|
if not self.issue_tracker_jira_password:
|
|
73
73
|
self.issue_tracker_jira_password = os.getenv("JIRA_PASSWORD", None)
|
|
74
74
|
|
|
75
|
+
self.stash_dir = self.__config.get("stash", {}).get("dir", ".kacl_stash")
|
|
76
|
+
self.stash_always = self.__config.get("stash", {}).get("always", False)
|
|
77
|
+
|
|
75
78
|
@staticmethod
|
|
76
79
|
def merge(a, b, path=None):
|
|
77
80
|
"""merge two dictionaries
|
kacl/document.py
CHANGED
|
@@ -4,6 +4,7 @@ import os
|
|
|
4
4
|
import git
|
|
5
5
|
import semver
|
|
6
6
|
|
|
7
|
+
import kacl
|
|
7
8
|
from kacl.config import KACLConfig
|
|
8
9
|
from kacl.element import KACLElement
|
|
9
10
|
from kacl.exception import KACLException
|
|
@@ -28,6 +29,7 @@ class KACLDocument:
|
|
|
28
29
|
versions=None,
|
|
29
30
|
link_references=None,
|
|
30
31
|
meta_data=None,
|
|
32
|
+
file_path=None,
|
|
31
33
|
config=KACLConfig(),
|
|
32
34
|
):
|
|
33
35
|
self.__data = data
|
|
@@ -38,6 +40,80 @@ class KACLDocument:
|
|
|
38
40
|
self.__link_references = dict()
|
|
39
41
|
self.__meta_data = meta_data
|
|
40
42
|
self.config = config
|
|
43
|
+
self.stash = []
|
|
44
|
+
self.file_path = file_path
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def stash_directory(self):
|
|
48
|
+
return self._ensure_stash_directory()
|
|
49
|
+
|
|
50
|
+
def load_stash(self):
|
|
51
|
+
self._refresh_stash()
|
|
52
|
+
# get all unreleased changes from stash
|
|
53
|
+
for stashed_changelog in self.stash:
|
|
54
|
+
unreleased_stash_version = stashed_changelog.get("Unreleased")
|
|
55
|
+
for title, change in unreleased_stash_version.sections().items():
|
|
56
|
+
for change_item in change.items():
|
|
57
|
+
self.add(section=title, data=change_item)
|
|
58
|
+
|
|
59
|
+
def _refresh_stash(self):
|
|
60
|
+
if not self.file_path:
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
self.stash = []
|
|
64
|
+
if self.file_path is None:
|
|
65
|
+
local_stash_dir = os.path.join(
|
|
66
|
+
os.path.abspath(os.getcwd()), self.config.stash_dir
|
|
67
|
+
)
|
|
68
|
+
else:
|
|
69
|
+
# the stash needs always to be relative to the current file_path
|
|
70
|
+
local_stash_dir = os.path.join(
|
|
71
|
+
os.path.abspath(os.path.dirname(self.file_path)), self.config.stash_dir
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
if not os.path.isdir(local_stash_dir):
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
# read all md files in the stash directory
|
|
78
|
+
for f in os.listdir(local_stash_dir):
|
|
79
|
+
if f.endswith(".md"):
|
|
80
|
+
md_file = os.path.join(local_stash_dir, f)
|
|
81
|
+
with open(md_file, "r") as fh:
|
|
82
|
+
data = fh.read()
|
|
83
|
+
try:
|
|
84
|
+
doc = KACLDocument.parse(data, None)
|
|
85
|
+
self.stash.append(doc)
|
|
86
|
+
except Exception:
|
|
87
|
+
continue
|
|
88
|
+
|
|
89
|
+
def _ensure_stash_directory(self):
|
|
90
|
+
# the stash needs always to be relative to the current file_path
|
|
91
|
+
local_stash_dir = os.path.join(
|
|
92
|
+
os.path.abspath(os.path.dirname(self.file_path)), self.config.stash_dir
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
if not os.path.isdir(local_stash_dir):
|
|
96
|
+
os.makedirs(local_stash_dir, exist_ok=True)
|
|
97
|
+
|
|
98
|
+
return local_stash_dir
|
|
99
|
+
|
|
100
|
+
def _get_stash_file(self):
|
|
101
|
+
# the stash file should be named after the current git branch
|
|
102
|
+
local_stash_dir = self._ensure_stash_directory()
|
|
103
|
+
try:
|
|
104
|
+
repo = git.Repo(
|
|
105
|
+
os.path.abspath(os.path.dirname(self.file_path)),
|
|
106
|
+
search_parent_directories=True,
|
|
107
|
+
)
|
|
108
|
+
branch_name = repo.active_branch.name
|
|
109
|
+
# sanitize branch name for filename
|
|
110
|
+
branch_name = branch_name.replace("/", "__").replace("\\", "_")
|
|
111
|
+
stash_file_name = f"{branch_name}.md"
|
|
112
|
+
except Exception:
|
|
113
|
+
# fallback to with date
|
|
114
|
+
stash_file_name = f"{datetime.datetime.now().strftime('%Y%m%d')}.md"
|
|
115
|
+
|
|
116
|
+
return os.path.join(local_stash_dir, stash_file_name)
|
|
41
117
|
|
|
42
118
|
def validate(self):
|
|
43
119
|
"""Validates the current changelog and returns KACLValidation object containing all information
|
|
@@ -46,6 +122,14 @@ class KACLDocument:
|
|
|
46
122
|
[KACLValidation] -- object holding all error information
|
|
47
123
|
"""
|
|
48
124
|
validation = KACLValidation()
|
|
125
|
+
|
|
126
|
+
# first load the stash
|
|
127
|
+
self._refresh_stash()
|
|
128
|
+
for stashed_changelog in self.stash:
|
|
129
|
+
val = stashed_changelog.validate()
|
|
130
|
+
for e in val.errors():
|
|
131
|
+
validation.add(e)
|
|
132
|
+
|
|
49
133
|
# 1. assert only one header and starts on first line
|
|
50
134
|
if len(self.__headers) == 0:
|
|
51
135
|
validation.add_error(
|
|
@@ -213,18 +297,38 @@ class KACLDocument:
|
|
|
213
297
|
|
|
214
298
|
return False
|
|
215
299
|
|
|
216
|
-
def add(self, section, data):
|
|
300
|
+
def add(self, section, data, stash=False):
|
|
217
301
|
"""adds a new change to a given section in the 'unreleased' version
|
|
218
302
|
|
|
219
303
|
Arguments:
|
|
220
304
|
section {[str]} -- section to add data to
|
|
221
305
|
data {[str]} -- change information
|
|
306
|
+
stash {[bool]} -- if true the change will be stashed instead of added to the changelog (default: {False})
|
|
222
307
|
"""
|
|
223
|
-
|
|
224
|
-
if
|
|
225
|
-
|
|
226
|
-
self.
|
|
227
|
-
|
|
308
|
+
|
|
309
|
+
if stash:
|
|
310
|
+
# check if the stash directory already contains the required changelog file, if not initialize it
|
|
311
|
+
stash_file = self._get_stash_file()
|
|
312
|
+
|
|
313
|
+
if not os.path.exists(stash_file):
|
|
314
|
+
kacl_changelog = kacl.new()
|
|
315
|
+
kacl_changelog_content = kacl.dump(kacl_changelog)
|
|
316
|
+
with open(stash_file, "w") as f:
|
|
317
|
+
f.write(kacl_changelog_content)
|
|
318
|
+
f.close()
|
|
319
|
+
|
|
320
|
+
kacl_changelog = kacl.load(stash_file)
|
|
321
|
+
kacl_changelog.add(section.capitalize(), data)
|
|
322
|
+
kacl_changelog_content = kacl.dump(kacl_changelog)
|
|
323
|
+
with open(stash_file, "w") as f:
|
|
324
|
+
f.write(kacl_changelog_content)
|
|
325
|
+
f.close()
|
|
326
|
+
else:
|
|
327
|
+
unreleased_version = self.get("Unreleased")
|
|
328
|
+
if unreleased_version is None:
|
|
329
|
+
unreleased_version = KACLVersion(version="Unreleased")
|
|
330
|
+
self.__versions.insert(0, unreleased_version)
|
|
331
|
+
unreleased_version.add(section.capitalize(), data)
|
|
228
332
|
|
|
229
333
|
def release(
|
|
230
334
|
self,
|
|
@@ -584,7 +688,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
|
584
688
|
)
|
|
585
689
|
|
|
586
690
|
@staticmethod
|
|
587
|
-
def parse(data):
|
|
691
|
+
def parse(data, file=None):
|
|
588
692
|
"""Parses a given text object and returns the KACLDocument
|
|
589
693
|
|
|
590
694
|
Arguments:
|
|
@@ -624,6 +728,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
|
624
728
|
versions=versions,
|
|
625
729
|
link_references=link_references,
|
|
626
730
|
meta_data=meta_data,
|
|
731
|
+
file_path=file,
|
|
627
732
|
)
|
|
628
733
|
|
|
629
734
|
def __get_link_provider(
|
kacl/kacl_cli.py
CHANGED
|
@@ -69,6 +69,7 @@ def load_changelog(ctx):
|
|
|
69
69
|
# read the changelog
|
|
70
70
|
kacl_changelog = kacl.load(kacl_config.changelog_file_path)
|
|
71
71
|
kacl_changelog.config = kacl_config
|
|
72
|
+
kacl_changelog.load_stash()
|
|
72
73
|
|
|
73
74
|
# share the objects
|
|
74
75
|
return kacl_changelog
|
|
@@ -128,14 +129,28 @@ def cli(ctx, version=None, config=None, file=None):
|
|
|
128
129
|
is_flag=True,
|
|
129
130
|
help="This option will add the changes directly into changelog file.",
|
|
130
131
|
)
|
|
131
|
-
|
|
132
|
+
@click.option(
|
|
133
|
+
"-s",
|
|
134
|
+
"--stash",
|
|
135
|
+
is_flag=True,
|
|
136
|
+
help="This option will stash changes instead of applying them.",
|
|
137
|
+
)
|
|
138
|
+
def add(ctx, section, message, modify, stash):
|
|
132
139
|
"""Adds a given message to a specified unreleased section. Use '--modify' to directly modify the changelog file."""
|
|
133
140
|
kacl_changelog = load_changelog(ctx)
|
|
134
141
|
|
|
142
|
+
if not modify:
|
|
143
|
+
stash = False
|
|
144
|
+
else:
|
|
145
|
+
# check if we need to stash
|
|
146
|
+
stash = stash or kacl_changelog.config.stash_always
|
|
147
|
+
|
|
135
148
|
# add changes to changelog
|
|
136
|
-
kacl_changelog.add(section=section, data=message)
|
|
149
|
+
kacl_changelog.add(section=section, data=message, stash=stash)
|
|
150
|
+
|
|
137
151
|
kacl_changelog_content = kacl.dump(kacl_changelog)
|
|
138
|
-
if modify
|
|
152
|
+
# if stashing is enabled, do not modify the changelog file
|
|
153
|
+
if modify and not stash:
|
|
139
154
|
with open(kacl_changelog.config.changelog_file_path, "w") as f:
|
|
140
155
|
f.write(kacl_changelog_content)
|
|
141
156
|
f.close()
|
|
@@ -472,6 +487,13 @@ def release(
|
|
|
472
487
|
if no_commit:
|
|
473
488
|
return
|
|
474
489
|
|
|
490
|
+
# delete all file in the stash directory
|
|
491
|
+
stash_files = []
|
|
492
|
+
for f in os.listdir(kacl_changelog.stash_directory):
|
|
493
|
+
stash_file = os.path.join(kacl_changelog.stash_directory, f)
|
|
494
|
+
os.remove(stash_file)
|
|
495
|
+
stash_files.append(stash_file)
|
|
496
|
+
|
|
475
497
|
if commit or tag or kacl_config.git_create_commit or kacl_config.git_create_tag:
|
|
476
498
|
vcs_context = {
|
|
477
499
|
"latest_version": latest_version,
|
|
@@ -516,6 +538,11 @@ def release(
|
|
|
516
538
|
repo.git.add(kacl_config.changelog_file_path)
|
|
517
539
|
for f in kacl_config.git_commit_additional_files:
|
|
518
540
|
repo.git.add(f)
|
|
541
|
+
for f in stash_files:
|
|
542
|
+
try:
|
|
543
|
+
repo.git.add(f)
|
|
544
|
+
except git.GitCommandError:
|
|
545
|
+
pass
|
|
519
546
|
repo.git.commit("-m", commit_message.format(**vcs_context))
|
|
520
547
|
|
|
521
548
|
if tag or kacl_config.git_create_tag:
|
|
@@ -618,6 +645,52 @@ def new(output_file):
|
|
|
618
645
|
click.echo(kacl_changelog_content)
|
|
619
646
|
|
|
620
647
|
|
|
648
|
+
@cli.command()
|
|
649
|
+
@click.option(
|
|
650
|
+
"-f",
|
|
651
|
+
"--force",
|
|
652
|
+
required=False,
|
|
653
|
+
is_flag=True,
|
|
654
|
+
help="Will overwrite existing files.",
|
|
655
|
+
)
|
|
656
|
+
def init(force):
|
|
657
|
+
"""Initializes a project with all necessary kacl setting."""
|
|
658
|
+
kacl_changelog = kacl.new()
|
|
659
|
+
kacl_changelog_content = kacl.dump(kacl_changelog)
|
|
660
|
+
|
|
661
|
+
if os.path.exists("CHANGELOG.md") and not force:
|
|
662
|
+
click.echo(
|
|
663
|
+
click.style("Error: ", fg="red")
|
|
664
|
+
+ "CHANGELOG.md already exists. Use '--force' to overwrite."
|
|
665
|
+
)
|
|
666
|
+
sys.exit(1)
|
|
667
|
+
|
|
668
|
+
if os.path.exists(".kacl.yml") and not force:
|
|
669
|
+
click.echo(
|
|
670
|
+
click.style("Error: ", fg="red")
|
|
671
|
+
+ ".kacl.yml already exists. Use '--force' to overwrite."
|
|
672
|
+
)
|
|
673
|
+
sys.exit(1)
|
|
674
|
+
|
|
675
|
+
with open("CHANGELOG.md", "w") as f:
|
|
676
|
+
f.write(kacl_changelog_content)
|
|
677
|
+
f.close()
|
|
678
|
+
|
|
679
|
+
default_config_file = os.path.join(
|
|
680
|
+
os.path.dirname(os.path.abspath(__file__)), "config", "kacl-default.yml"
|
|
681
|
+
)
|
|
682
|
+
|
|
683
|
+
# copy the default config file
|
|
684
|
+
if os.path.exists(default_config_file):
|
|
685
|
+
with open(default_config_file, "r") as df:
|
|
686
|
+
default_config_content = df.read()
|
|
687
|
+
df.close()
|
|
688
|
+
|
|
689
|
+
with open(".kacl.yml", "w") as cf:
|
|
690
|
+
cf.write(default_config_content)
|
|
691
|
+
cf.close()
|
|
692
|
+
|
|
693
|
+
|
|
621
694
|
@cli.command("add-comments")
|
|
622
695
|
@click.pass_context
|
|
623
696
|
@click.option(
|
kacl/serializer.py
CHANGED
|
@@ -19,7 +19,11 @@ class KACLMarkdownSerializer:
|
|
|
19
19
|
data.extend(serialized_metadata)
|
|
20
20
|
|
|
21
21
|
data.append(self.__serialize_header(document.header()))
|
|
22
|
-
|
|
22
|
+
# check if the body starts with a newline. if not add one
|
|
23
|
+
if document.header().body().startswith("\n"):
|
|
24
|
+
data.append(document.header().body())
|
|
25
|
+
else:
|
|
26
|
+
data.append("\n" + document.header().body())
|
|
23
27
|
|
|
24
28
|
for version in document.versions():
|
|
25
29
|
data.extend([self.__serialize_version(version), ""])
|
|
@@ -67,11 +71,12 @@ class KACLMarkdownSerializer:
|
|
|
67
71
|
return f"# {obj.title()}"
|
|
68
72
|
|
|
69
73
|
def __serialize_version(self, obj):
|
|
70
|
-
lines = [self.__serialize_header(obj)]
|
|
74
|
+
lines = [self.__serialize_header(obj), ""]
|
|
71
75
|
for title, changes in obj.sections().items():
|
|
72
76
|
lines.extend(
|
|
73
77
|
[
|
|
74
78
|
self.__serialize_header(changes),
|
|
79
|
+
"",
|
|
75
80
|
self.__serialize_list(changes.items()),
|
|
76
81
|
"",
|
|
77
82
|
]
|