packer-release 0.23.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.
packer/__init__.py ADDED
@@ -0,0 +1,7 @@
1
+ from packer.core import Packer
2
+ from packer.setup import main as setup
3
+ from packer.change import main as commit_change
4
+ from packer.custom_modules.et import load_config, simple_merge_settings, normalize_settings_keys, tree
5
+ from packer.exceptions import Global_exception_handler
6
+
7
+ __all__ = ['load_config', 'Packer', 'setup', 'commit_change', 'simple_merge_settings', 'normalize_settings_keys', 'Global_exception_handler', 'tree']
packer/actions.py ADDED
@@ -0,0 +1,25 @@
1
+ from pathlib import Path
2
+
3
+ from packer.config import Project, all_settings, send_notification, exception_handler
4
+ import sys
5
+
6
+ from packer.core import Packer
7
+
8
+
9
+ def run(new_version: dict, project_directory: Path, project_configuration: Project):
10
+
11
+ try:
12
+ packer = Packer(new_version, project_directory, **project_configuration.model_dump())
13
+
14
+ def packer_exception_handler(exc_type, exc_value, exc_traceback):
15
+ packer.revert_changes(False)
16
+ if all_settings.desktop_notifications:
17
+ send_notification('Packer encountered an error and reverted all changes.', 'error')
18
+ exception_handler.handle_exception(exc_type, exc_value, exc_traceback)
19
+
20
+ sys.excepthook = packer_exception_handler # replace the global exception handler with packer's to revert changes in case Packer was running.
21
+
22
+ packer.run()
23
+ except KeyboardInterrupt:
24
+ packer.print_and_log('Process interrupted by user!', [255, 255, 0], level=30)
25
+ packer.revert_changes()
@@ -0,0 +1,70 @@
1
+ # $program_name Update [$new_version]
2
+
3
+ $version_description
4
+
5
+ ## Installation
6
+
7
+ Available via:
8
+
9
+ * **GitHub:** [GitHub Releases](https://github.com/${github_repo_url}/releases/tag/${new_version})
10
+ * **Third-party website (GoFile):** [Archive]($gofile_download_url) (click the download button).
11
+
12
+ ### Binary Downloads
13
+
14
+ When downloading, please choose the correct build for your operating system:
15
+
16
+ | File Name | Platform | Description |
17
+ | --- | --- | --- |
18
+ | `$program_name` | Linux/macOS | Executable for Unix-based systems. |
19
+ | `$program_name.exe` | Windows | Executable for Windows systems. |
20
+
21
+ ### To install:
22
+
23
+ * **GitHub:**
24
+ Download the appropriate binary for your system from the [Releases page](https://github.com/${github_repo_url}/releases/tag/${new_version}).
25
+ * **Third-party website (GoFile):**
26
+ Head to the website [Archive]($gofile_download_url) and download the specific arhive with the appropriate version.
27
+
28
+ After installing, continue following instructions via the README.
29
+
30
+ ## Changes in $new_version
31
+
32
+ $latest_changelog
33
+
34
+ [Full changelog](https://github.com/${github_repo_url}/blob/master/CHANGELOG.md)
35
+
36
+ ## Tips
37
+
38
+ If you prefer to build from source via **GitHub**, you can clone the repository using:
39
+
40
+ ```bash
41
+ git clone https://github.com/${github_repo_url} --depth 1
42
+
43
+ ```
44
+
45
+ Using `--depth 1` creates a "shallow clone," which only downloads the latest commit, saving you significant time and storage space compared to a full repository clone.
46
+
47
+ While downloading the pre-compiled binaries (like `$program_name` or `$program_name.exe`) is usually the fastest way to get started, cloning the repository allows you to easily pull future updates with `git pull` as they are released.
48
+
49
+ ## Verification
50
+
51
+ To ensure the integrity of the downloaded files, you can verify their SHA256 checksums.
52
+
53
+ ### How to verify:
54
+
55
+ * **Windows (PowerShell):**
56
+ Open PowerShell and run the following command:
57
+ ```powershell
58
+ Get-FileHash .\$program_name.exe -Algorithm SHA256
59
+
60
+ ```
61
+
62
+
63
+ * **Linux/macOS (Terminal):**
64
+ Open your terminal and run:
65
+ ```bash
66
+ sha256sum $program_name
67
+
68
+ ```
69
+
70
+ Compare the resulting hash with the one provided in the GitHub release assets list to ensure it matches.
Binary file
Binary file
@@ -0,0 +1 @@
1
+ {"CWD": {"root_hash": "8630217fe25a8f5cffc622b9825526bc14ba74e1a5c0962da2eb4699b84232ea", "tree": {"files": {".gitignore": "d7ba4a10f60365e1b1ebad9c6df3aea7acbd0ae29b3732c614db22d65e0f89fc", "CHANGELOG.md": "bf7d79f61f5080f8bd48996e42d1a6cd14611f12ebb5e61d455391b054f3d84f", "LICENSE": "bf1c380e8205fa5c7872101cd09794e8c1d313d4326c2a7a21a9c150584d2f96", "README.md": "aa3284a20fe8f30bad89b895ccd5473ff6c4c66c4a098937a5a34f2a2ac64cd2", "main.spec": "cc61795424a36b70d1c9d1b0051e445c55610f1a45389206a8f6500855ccc00b", "pyproject.toml": "68e06cb39201354dae20165ec1494116310fa85687da883bb04814c5c84af062"}, ".github": {"workflows": {"files": {"build.yaml": "a5c406080d9bb26d57ef5a6c265ae145309171f3087a6daac63a97494ad196b6"}}}, "docs": {"files": {"CLI.md": "acfcc4d27a6789e3b90cd12ecbb94a7602bead7562cd123e5b68ee6add353fda", "MANUAL_REVERT.md": "ff061a209167f7ab303601ef3a49b147fad54616b955e53a73e103bd332eeac4", "PROJECT.md": "25021c22dfdd43b862f83ca182c528f92d6c5066e29488eeb1a20cea092051b5", "ROADMAP.md": "7aa1b20b64eda32e2482e649932d4bef3b98ebf58de6404f16d03bb018bde8a7", "SETTINGS.md": "0c3625de0701598340879af51261b970135f22ecedd3c677bb8bbdedd1e1be01"}}, "src": {"packer": {"files": {"__init__.py": "2f79f39d09df2cf1f2244835e20904c382ea1344851349a49908beb0a0a4e5a0", "actions.py": "062ec62bcd99b75d679a69aa01c1e9c1afba723bb4ef8480e5236df6de57a634", "change.py": "502cf95c317be92656611a15f9a4292e26d8ce5bf1f011454ab1ef58a8a07a7f", "config.py": "d07e0fd1aa831e649491a459f286f8dade61bbbe043084fd61fa95775607ee52", "core.py": "d9946fb705161189172168f71cbe60481bb656c639d28d77c62740a1498a3c7d", "exceptions.py": "cc4af0b3a01f920badae03f9cc4a702363594d8a77a7a3a0469418bd88b5bb87", "main.py": "5a6218853b4961eefe4100531614a4f889c8b8a6aef03c9071e13b0539f3a512", "paths.py": "691b5fb411de0194382b698754bc0b29969c401823547e5ad7eddfb5ade18322", "setup.py": "a2cecc29f53a8daac96629b015c3eeb0531d115499df7500e3918a1357f0f903", "utils.py": "715ebf84d5b611c97bdcd27e79d42a8259c9170d12902ffd1ff51cf8bea15e78"}, "assets": {"files": {"RELEASE.md": "22f25566487db873890765dd0d98059ad59358606db52daa175b2d0c0500f703", "integrity.json": "4b8972f0d88174e1db151260410eb0c2a16aa70293baef224ffe5ebff3009672", "version.json": "6a079cf0fad3cb40c726916a02a31997fb2c41e61de8192a8a919bccb5909931"}, "audio": {"sounds": {"files": {"new notification 1.wav": "47a227b0403611b4d650dd4322e8784ac8154ac80abbe73b1c259df755daf8da", "new notification 2.wav": "d7a0e41e28cdbaf92fcf77da82f1656c5284e79557f5bdd143d504bb00c3aa81", "new notification 3.wav": "0b3200bd3c279b4acc901c35cd899b1e3da62b6283bf34b8aea96fb82987dae4"}}}, "images": {"files": {"Packer icon.ico": "311cfef9f20c3d377bc3d3b19751a67f66e373cd4c6a5c80294e3dee3deca947", "Packer icon.png": "1d80eca39a6ff0e147ab8795fc6dc2d01b67e5a447780ba94dcee50f86ebd715"}}}, "custom_modules": {"files": {"et.py": "e4c6fb8d81b35f82f8dc6f4940fea9800e2bed2f7aaac1a7e84149852e8c3ce6", "etf.py": "885036a0b0ccd854eb4e4d93d4b2df815984d1a19f4c6ae628379e5ad731af94"}}, "ui": {"files": {"cli.py": "3775d67cf64fe28b39eabef33a5aa985a32b5e06fe2d3a14293d4b72ab8fac6e", "tui.py": "3a81c5e6bede48e2d31bac5ff950d7b110640bd17edcb4bdcf7789c2d77f8d40"}}}}}}}
@@ -0,0 +1,5 @@
1
+ {
2
+ "major": 0,
3
+ "minor": 23,
4
+ "patch": 0
5
+ }
packer/change.py ADDED
@@ -0,0 +1,222 @@
1
+ from string import Template
2
+ from subprocess import run
3
+ from pathlib import Path
4
+ from git import Repo
5
+ from ollama import chat
6
+
7
+ from packer.config import _input_via_text_editor, all_settings
8
+
9
+ def main(git_directory: str | Path = '.', modification_types: list[str] = ['c'], overall_description: str = None,
10
+ text_editor: str = all_settings.text_editor, wait_flag: str = all_settings.wait_flag,
11
+ ai_summary: bool = True,
12
+ check_todo: bool = True, todo_rel_path: str = '/dev/TODO.md', list_start_identifier: str = 'before committing', list_end_identifier: str = '#',
13
+ bullet_summary_prompt = all_settings.changes_summary_prompt, high_level_summary_prompt = all_settings.high_level_summary_prompt,
14
+ model: str = all_settings.model, verbose: bool = True):
15
+ '''
16
+ Opens a temporary file in the specified text editor for describing the project's modification
17
+
18
+ This function creates a temporary file, opens it in the specified text editor,
19
+ waits for the editor to close, and then removes the temporary file. Afterwards updates the changelog
20
+ and commits it with the commit message as the project's entered modification as the user had.
21
+
22
+ :param git_directory: The root directory of the project (default is '.')
23
+ :type git_directory: str | Path
24
+ :param modification_types: The types of modifications to record (a = added, c = changed, f = fixed) (default is ['c'])
25
+ :type modification_types: list[str]
26
+ :param overall_description: The overall description of the modifications, if there are multiple (default is None)
27
+ :type overall_description: str
28
+ :param ai_summary: Whether to generate an AI summary of the changes (default is True)
29
+ :type ai_summary: bool
30
+ :param check_todo: Whether to check for TODO items before committing (default is True)
31
+ :type check_todo: bool
32
+ :param todo_rel_path: The relative path to the TODO file (default is '/dev/TODO.md')
33
+ :type todo_rel_path: str
34
+ :param list_start_identifier: The identifier for the start of the TODO list (default is 'before committing')
35
+ :type list_start_identifier: str
36
+ :param list_end_identifier: The identifier for the end of the TODO list (default is '#')
37
+ :type list_end_identifier: str
38
+ :param verbose: Whether to print verbose output (default is True)
39
+ :type verbose: bool
40
+ :param bullet_summary_prompt: The prompt template for generating bullet point summaries (default is all_settings.changes_summary_prompt)
41
+ :type bullet_summary_prompt: list[dict]
42
+ :param high_level_summary_prompt: The prompt template for generating high-level summaries (default is all_settings.high_level_summary_prompt)
43
+ :type high_level_summary_prompt: list[dict]
44
+ :param model: The AI model to use for generating summaries (default is all_settings.model)
45
+ :type model: str
46
+ '''
47
+
48
+ def input_via_text_editor(message: str) -> str:
49
+ '''
50
+ Opens a temporary file in the specified text editor for user input.
51
+
52
+ :param message: The initial message to display in the text editor
53
+ :type message: str
54
+ :return: The user's input from the text editor
55
+ :rtype: str
56
+ '''
57
+ return _input_via_text_editor(message, text_editor=text_editor, wait_flag=wait_flag)
58
+
59
+ if check_todo:
60
+ with open(f'{git_directory}{todo_rel_path}') as f:
61
+ content = f.read().lower()
62
+
63
+ list_start = content.find(list_start_identifier)
64
+ list_end = content.find(list_end_identifier, list_start)
65
+
66
+ before_committing_list = content[list_start + len(list_start_identifier):list_end].lstrip(':').strip()
67
+
68
+ if before_committing_list:
69
+ print(f'{list_start_identifier} task/s found, please delete them from the list once finished!\nList:\n{before_committing_list}')
70
+ return
71
+
72
+
73
+ repo = Repo(git_directory)
74
+ diff_text = repo.git.diff(unified=3)
75
+
76
+ if ai_summary:
77
+ # Update prompts with runtime data
78
+ summary_data = {
79
+ 'diff': diff_text,
80
+ 'changes': ','.join(modification_types)
81
+ }
82
+
83
+ bullet_summary_prompt[1]['content'] = Template(bullet_summary_prompt[1 if bullet_summary_prompt[1]['role'] == 'user' else 0]['content']).substitute(summary_data)
84
+ if verbose:
85
+ print('Generating bullet summary...')
86
+ bullet_summary = chat(
87
+ model=model,
88
+ messages=bullet_summary_prompt
89
+ )['message']['content'].strip()
90
+
91
+
92
+ high_level_summary_data = {
93
+ 'bullet_summary': bullet_summary
94
+ }
95
+
96
+ high_level_summary_prompt[1]['content'] = Template(high_level_summary_prompt[1 if high_level_summary_prompt[1]['role'] == 'user' else 0]['content']).substitute(high_level_summary_data)
97
+ if verbose:
98
+ print('Generating high level summary...')
99
+ high_level_summary = chat(
100
+ model=model,
101
+ messages=high_level_summary_prompt
102
+ )['message']['content'].strip()
103
+
104
+
105
+
106
+ with open(f'{git_directory}/CHANGELOG.md', 'r') as f:
107
+ full_changelog = f.read()
108
+
109
+ messages = []
110
+
111
+ for i, modification_type in enumerate(modification_types):
112
+
113
+ if ai_summary:
114
+ bullet_summary_list = bullet_summary.splitlines()
115
+ message = bullet_summary_list[i] if len(bullet_summary_list) > i else 'AI didn\'t generate anything...'
116
+ else:
117
+ message = ''
118
+
119
+ message = input_via_text_editor(message)
120
+
121
+ match modification_type:
122
+ case 'a':
123
+ modification_type = 'Added'
124
+ end = full_changelog.find('### Changed') - 2
125
+ case 'c':
126
+ end = full_changelog.find('### Fixed') - 2
127
+ modification_type = 'Changed'
128
+ case 'f':
129
+ end = full_changelog.find('---') - 2
130
+ modification_type = 'Fixed'
131
+
132
+ message = message.lstrip('-')
133
+ message = message.lstrip()
134
+ if message.lower().startswith(modification_type.lower()):
135
+ message = message[len(modification_type):].lstrip()
136
+ message = message.strip()
137
+
138
+ message = f'{message[:1].capitalize()}{message[1:]}'
139
+ if not message.endswith('.'):
140
+ message = f'{message}.'
141
+
142
+ messages.append([modification_type, message])
143
+
144
+ full_changelog = f'{full_changelog[0: end]}\n- {message} {full_changelog[end:]}'
145
+ with open(f'{git_directory}/CHANGELOG.md', 'w') as f:
146
+ f.write(full_changelog)
147
+
148
+ if len(messages) > 1:
149
+ git_message = f'feat: {input_via_text_editor(high_level_summary if ai_summary else overall_description)}\n\n'
150
+ for message in messages:
151
+ git_message += f'- {message[0]}: {message[1]}\n'
152
+ else:
153
+ git_message = f'{messages[0][0]}: {messages[0][1]}'
154
+
155
+ run(['git', 'add', '.'])
156
+ run(['git', 'commit', '-m', git_message.strip()])
157
+ run(['git', 'push'])
158
+
159
+ def tui(changes: list[str] | None = None, overall_description: str | None = None, ai_suggestions: bool = True):
160
+ '''
161
+ Launches a text-based user interface for creating project change logs and committing them.
162
+
163
+ This function is intended to be used as an entry point for a TUI that guides the user
164
+ through creating changelog entries and committing them to the repository.
165
+
166
+ :param changes: A list of modification types (a = added, c = changed, f = fixed) (default is None)
167
+ :type changes: list[str] | None
168
+ :param overall_description: An overall description of the modifications, if there are multiple (default is None)
169
+ :type overall_description: str | None
170
+ :param ai_suggestions: Whether to generate AI suggestions for the changes (default is True)
171
+ :type ai_suggestions: bool
172
+ '''
173
+
174
+ if ai_suggestions:
175
+ try:
176
+ print(f'AI suggestions:\n{generate_suggestions()}')
177
+ except KeyboardInterrupt:
178
+ print('Skipping AI suggestions...')
179
+
180
+ if not changes:
181
+ amount = int(input('Enter the amount of changes: '))
182
+ if amount > 1:
183
+ print('Enter each change idvidually, both in terminal and text editor.')
184
+
185
+ changes = []
186
+ for _ in range(amount):
187
+ changes.append(input('Enter the modification type [a, c, f] ').strip().lower())
188
+
189
+ if not overall_description:
190
+ if len(changes) > 1:
191
+ overall_description = input('High level description for all changes together [None]: ').strip()
192
+ else:
193
+ overall_description = None
194
+
195
+ return (changes, overall_description)
196
+
197
+ def generate_suggestions(git_directory: str | Path = '.', suggestions_prompt = all_settings.suggestions_prompt, model: str = all_settings.model, verbose: bool = True):
198
+ '''
199
+ Generates AI suggestions for the changes based on the current git diff.
200
+
201
+ :param git_directory: The root directory of the project (default is '.')
202
+ :type git_directory: str | Path
203
+ :param suggestions_prompt: The prompt template for generating AI suggestions (default is all_settings.suggestions_prompt)
204
+ :type suggestions_prompt: list[dict]
205
+ :param model: The AI model to use for generating suggestions (default is all_settings.model)
206
+ :type model: str
207
+ '''
208
+ repo = Repo(git_directory)
209
+ diff_text = repo.git.diff(unified=3)
210
+
211
+ suggestions_prompt[1]['content'] = Template(suggestions_prompt[1 if suggestions_prompt[1]['role'] == 'user' else 0]['content']).substitute({'diff': diff_text})
212
+
213
+ if verbose:
214
+ print('Generating AI suggestions...')
215
+ return chat(
216
+ model=model,
217
+ messages=suggestions_prompt
218
+ )['message']['content'].strip()
219
+
220
+ if __name__ == '__main__':
221
+ output = tui()
222
+ main(modification_types=output[0], overall_description=output[1])