pyproject-appimage 3.1__tar.gz → 4.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.
- {pyproject-appimage-3.1/pyproject_appimage.egg-info → pyproject-appimage-4.0}/PKG-INFO +3 -1
- {pyproject-appimage-3.1 → pyproject-appimage-4.0}/README.md +1 -0
- {pyproject-appimage-3.1 → pyproject-appimage-4.0}/pyproject.toml +14 -10
- {pyproject-appimage-3.1 → pyproject-appimage-4.0}/pyproject_appimage/__init__.py +107 -56
- pyproject-appimage-4.0/pyproject_appimage/version.txt +1 -0
- {pyproject-appimage-3.1 → pyproject-appimage-4.0/pyproject_appimage.egg-info}/PKG-INFO +3 -1
- {pyproject-appimage-3.1 → pyproject-appimage-4.0}/pyproject_appimage.egg-info/SOURCES.txt +0 -1
- {pyproject-appimage-3.1 → pyproject-appimage-4.0}/pyproject_appimage.egg-info/requires.txt +1 -0
- pyproject-appimage-3.1/pyproject_appimage/default.desktop +0 -6
- pyproject-appimage-3.1/pyproject_appimage/version.txt +0 -1
- {pyproject-appimage-3.1 → pyproject-appimage-4.0}/LICENSE +0 -0
- {pyproject-appimage-3.1 → pyproject-appimage-4.0}/pyproject_appimage/__main__.py +0 -0
- {pyproject-appimage-3.1 → pyproject-appimage-4.0}/pyproject_appimage/default.png +0 -0
- {pyproject-appimage-3.1 → pyproject-appimage-4.0}/pyproject_appimage.egg-info/dependency_links.txt +0 -0
- {pyproject-appimage-3.1 → pyproject-appimage-4.0}/pyproject_appimage.egg-info/entry_points.txt +0 -0
- {pyproject-appimage-3.1 → pyproject-appimage-4.0}/pyproject_appimage.egg-info/top_level.txt +0 -0
- {pyproject-appimage-3.1 → pyproject-appimage-4.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pyproject-appimage
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.0
|
|
4
4
|
Summary: Generate AppImages from your Python projects
|
|
5
5
|
Author-email: JakobDev <jakobdev@gmx.de>
|
|
6
6
|
License: BSD-2-Clause
|
|
@@ -25,6 +25,7 @@ Requires-Python: >=3.9
|
|
|
25
25
|
Description-Content-Type: text/markdown
|
|
26
26
|
License-File: LICENSE
|
|
27
27
|
Requires-Dist: requests
|
|
28
|
+
Requires-Dist: desktop-entry-lib
|
|
28
29
|
Requires-Dist: tomli; python_version < "3.11"
|
|
29
30
|
|
|
30
31
|
# pyproject-appimage
|
|
@@ -66,6 +67,7 @@ The following options can be used in your pyproject.toml:
|
|
|
66
67
|
| python-version | string | The Python version that is used. Default is your current version. Can be overwritten with the cli. |
|
|
67
68
|
| updateinformation | string | The [update information](https://github.com/AppImage/AppImageSpec/blob/master/draft.md#update-information)
|
|
68
69
|
| compression | string | The Squashfs compression
|
|
70
|
+
| additional-packages | list of strins | A list of packages that should also be installed
|
|
69
71
|
|
|
70
72
|
Note: All paths are relativ to your project directory
|
|
71
73
|
|
|
@@ -37,6 +37,7 @@ The following options can be used in your pyproject.toml:
|
|
|
37
37
|
| python-version | string | The Python version that is used. Default is your current version. Can be overwritten with the cli. |
|
|
38
38
|
| updateinformation | string | The [update information](https://github.com/AppImage/AppImageSpec/blob/master/draft.md#update-information)
|
|
39
39
|
| compression | string | The Squashfs compression
|
|
40
|
+
| additional-packages | list of strins | A list of packages that should also be installed
|
|
40
41
|
|
|
41
42
|
Note: All paths are relativ to your project directory
|
|
42
43
|
|
|
@@ -28,26 +28,30 @@ classifiers = [
|
|
|
28
28
|
]
|
|
29
29
|
dependencies = [
|
|
30
30
|
"requests",
|
|
31
|
+
"desktop-entry-lib",
|
|
31
32
|
"tomli; python_version < '3.11'"
|
|
32
33
|
]
|
|
33
34
|
dynamic = ["version"]
|
|
34
35
|
|
|
35
|
-
[tool.setuptools.dynamic]
|
|
36
|
-
version = { file = "pyproject_appimage/version.txt" }
|
|
37
|
-
|
|
38
|
-
[project.scripts]
|
|
39
|
-
pyproject-appimage = "pyproject_appimage:main"
|
|
40
|
-
|
|
41
|
-
[tool.setuptools.package-data]
|
|
42
|
-
pyproject_appimage = ["version.txt", "default.png", "default.desktop"]
|
|
43
|
-
|
|
44
36
|
[project.urls]
|
|
45
37
|
Downloads = "https://codeberg.org/JakobDev/pyproject-appimage/releases"
|
|
46
38
|
Issues = "https://codeberg.org/JakobDev/pyproject-appimage/issues"
|
|
47
39
|
Source = "https://codeberg.org/JakobDev/pyproject-appimage"
|
|
48
40
|
Donation = "https://ko-fi.com/jakobdev"
|
|
49
41
|
|
|
42
|
+
[tool.setuptools.package-dir]
|
|
43
|
+
pyproject_appimage = "pyproject_appimage"
|
|
44
|
+
|
|
45
|
+
[tool.setuptools.dynamic]
|
|
46
|
+
version = { file = "pyproject_appimage/version.txt" }
|
|
47
|
+
|
|
48
|
+
[tool.setuptools.package-data]
|
|
49
|
+
pyproject_appimage = ["version.txt", "default.png"]
|
|
50
|
+
|
|
51
|
+
[project.scripts]
|
|
52
|
+
pyproject-appimage = "pyproject_appimage:main"
|
|
53
|
+
|
|
50
54
|
[tool.pyproject-appimage]
|
|
51
55
|
script = "pyproject-appimage"
|
|
52
56
|
output = "pyproject-appimage.AppImage"
|
|
53
|
-
python-version ="3.
|
|
57
|
+
python-version ="3.12"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from typing import Optional, Any, TypedDict
|
|
2
|
+
import desktop_entry_lib
|
|
2
3
|
import subprocess
|
|
3
4
|
import argparse
|
|
4
5
|
import requests
|
|
@@ -34,6 +35,18 @@ PyprojectDict = TypedDict("PyprojectDict", {
|
|
|
34
35
|
}, total=False)
|
|
35
36
|
|
|
36
37
|
|
|
38
|
+
def read_pyproject_file(path: str) -> dict[str, Any]:
|
|
39
|
+
with open(path, "rb") as f:
|
|
40
|
+
try:
|
|
41
|
+
return toml_load(f)
|
|
42
|
+
except Exception as ex:
|
|
43
|
+
if len(ex.args) == 1:
|
|
44
|
+
print("Error while parsing " + os.path.join(args.project_dir, "pyproject.toml") + f": {ex.args[0]}", file=sys.stderr)
|
|
45
|
+
else:
|
|
46
|
+
print("Error while parsing " + os.path.join(args.project_dir, "pyproject.toml"), file=sys.stderr)
|
|
47
|
+
sys.exit(1)
|
|
48
|
+
|
|
49
|
+
|
|
37
50
|
def check_key(project_dir: str, pyproject: PyprojectDict, key: str, error_list: list[str], checks: list[str]) -> None:
|
|
38
51
|
if key not in pyproject:
|
|
39
52
|
if "required" in checks:
|
|
@@ -50,6 +63,13 @@ def check_key(project_dir: str, pyproject: PyprojectDict, key: str, error_list:
|
|
|
50
63
|
if "bool" in checks:
|
|
51
64
|
if not isinstance(pyproject[key], bool):
|
|
52
65
|
error_list.append(f"\"{key}\" must be a bool")
|
|
66
|
+
if "string-list" in checks:
|
|
67
|
+
if not isinstance(pyproject[key], list):
|
|
68
|
+
error_list.append(f"\"{key}\" must be a list of strings")
|
|
69
|
+
else:
|
|
70
|
+
for i in pyproject[key]:
|
|
71
|
+
if not isinstance(i, str):
|
|
72
|
+
error_list.append(f"\"{key}\" must be a list of strings")
|
|
53
73
|
|
|
54
74
|
|
|
55
75
|
def check_pyproject(project_dir: str, pyproject: PyprojectDict) -> None:
|
|
@@ -69,6 +89,7 @@ def check_pyproject(project_dir: str, pyproject: PyprojectDict) -> None:
|
|
|
69
89
|
check_key(project_dir, pyproject, "output", error_list, ["string"])
|
|
70
90
|
check_key(project_dir, pyproject, "updateinformation", error_list, ["string"])
|
|
71
91
|
check_key(project_dir, pyproject, "compression", error_list, ["string"])
|
|
92
|
+
check_key(project_dir, pyproject, "additional-packages", error_list, ["string-list"])
|
|
72
93
|
|
|
73
94
|
if ("gettext-desktop-entry" in pyproject or "gettext-appstream" in pyproject) and not "gettext-directory" in pyproject:
|
|
74
95
|
error_list.append("\"gettext-directory\" must be set when using gettext")
|
|
@@ -122,6 +143,84 @@ def get_icon_size(work_dir: str, icon_path: str) -> tuple[int, int]:
|
|
|
122
143
|
return (int(width), int(height))
|
|
123
144
|
|
|
124
145
|
|
|
146
|
+
def handle_icon(project_dir: str, work_dir: str, app_root: str, pyproject: PyprojectDict) -> None:
|
|
147
|
+
if "icon" not in pyproject:
|
|
148
|
+
shutil.copyfile(os.path.join(os.path.dirname(__file__), "default.png"), os.path.join(app_root, ".DirIcon"))
|
|
149
|
+
shutil.copyfile(os.path.join(os.path.dirname(__file__), "default.png"), os.path.join(app_root, "pyproject-appimage-default.png"))
|
|
150
|
+
return
|
|
151
|
+
|
|
152
|
+
icon_path = os.path.join(project_dir, pyproject["icon"])
|
|
153
|
+
|
|
154
|
+
if icon_path.endswith(".png"):
|
|
155
|
+
shutil.copyfile(icon_path, os.path.join(app_root, ".DirIcon"))
|
|
156
|
+
else:
|
|
157
|
+
# If a Icon is not PNG, convert it
|
|
158
|
+
if icon_path.endswith(".svg"):
|
|
159
|
+
# https://stackoverflow.com/a/55370062
|
|
160
|
+
subprocess.run([get_image_magick_command(work_dir), "-background", "none", icon_path, "PNG:" + os.path.join(app_root, ".DirIcon")], check=True)
|
|
161
|
+
else:
|
|
162
|
+
subprocess.run([get_image_magick_command(work_dir), icon_path, "PNG:" + os.path.join(app_root, ".DirIcon")], check=True)
|
|
163
|
+
|
|
164
|
+
if "rename-icon" in pyproject:
|
|
165
|
+
icon_name = pyproject["rename-icon"]
|
|
166
|
+
else:
|
|
167
|
+
icon_name = os.path.basename(pyproject["icon"])
|
|
168
|
+
|
|
169
|
+
shutil.copyfile(icon_path, os.path.join(app_root, icon_name))
|
|
170
|
+
|
|
171
|
+
if icon_name.endswith(".svg"):
|
|
172
|
+
icon_dir = os.path.join(app_root, "usr", "share", "icons", "hicolor", "scalable", "apps")
|
|
173
|
+
else:
|
|
174
|
+
icon_size = get_icon_size(work_dir, os.path.join(app_root, icon_name))
|
|
175
|
+
icon_dir = os.path.join(app_root, "usr", "share", "icons", "hicolor", f"{icon_size[0]}x{icon_size[1]}", "apps")
|
|
176
|
+
|
|
177
|
+
if not os.path.isdir(icon_dir):
|
|
178
|
+
os.makedirs(icon_dir)
|
|
179
|
+
|
|
180
|
+
shutil.copyfile(os.path.join(app_root, icon_name), os.path.join(icon_dir, icon_name))
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def create_desktop_entry(project_dir: str, app_root: str, pyproject: PyprojectDict) -> None:
|
|
184
|
+
full_pyproject = read_pyproject_file(os.path.join(project_dir, "pyproject.toml"))
|
|
185
|
+
|
|
186
|
+
entry = desktop_entry_lib.DesktopEntry()
|
|
187
|
+
|
|
188
|
+
if "project" in full_pyproject:
|
|
189
|
+
entry.Name.default_text = full_pyproject["project"].get("name", "App")
|
|
190
|
+
if "description" in full_pyproject["project"]:
|
|
191
|
+
entry.Comment.default_text = full_pyproject["project"]["description"]
|
|
192
|
+
else:
|
|
193
|
+
entry.Name.default_text = "App"
|
|
194
|
+
|
|
195
|
+
entry.Icon = "pyproject-appimage-default"
|
|
196
|
+
entry.Exec = pyproject["script"]
|
|
197
|
+
|
|
198
|
+
entry.Categories.append("Utility")
|
|
199
|
+
|
|
200
|
+
entry.write_file(os.path.join(app_root, "pyproject-appimage-default.desktop"))
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def handle_desktop_entry(project_dir: str, app_root: str, pyproject: PyprojectDict) -> None:
|
|
204
|
+
if "desktop-entry" not in pyproject:
|
|
205
|
+
create_desktop_entry(project_dir, app_root, pyproject)
|
|
206
|
+
return
|
|
207
|
+
|
|
208
|
+
desktop_source_path = os.path.join(project_dir, pyproject["desktop-entry"])
|
|
209
|
+
desktop_dest_path = os.path.join(app_root, pyproject.get("rename-desktop-entry", os.path.basename(pyproject["desktop-entry"])))
|
|
210
|
+
|
|
211
|
+
if pyproject.get("gettext-desktop-entry", False):
|
|
212
|
+
subprocess.run(["msgfmt", "--desktop", "--template", desktop_source_path, "-d", os.path.join(project_dir, pyproject["gettext-directory"]), "-o", desktop_dest_path], check=True)
|
|
213
|
+
else:
|
|
214
|
+
shutil.copyfile(desktop_source_path, desktop_dest_path)
|
|
215
|
+
|
|
216
|
+
desktop_share_dir = os.path.join(app_root, "usr", "share", "applications")
|
|
217
|
+
|
|
218
|
+
if not os.path.isdir(desktop_share_dir):
|
|
219
|
+
os.makedirs(desktop_share_dir)
|
|
220
|
+
|
|
221
|
+
shutil.copyfile(desktop_dest_path, os.path.join(desktop_share_dir, os.path.basename(desktop_dest_path)))
|
|
222
|
+
|
|
223
|
+
|
|
125
224
|
def build_appimage(project_dir: str, work_dir: str, pyproject: PyprojectDict, args: argparse.Namespace) -> None:
|
|
126
225
|
if args.python_version is not None:
|
|
127
226
|
python_version = args.python_version
|
|
@@ -164,52 +263,9 @@ def build_appimage(project_dir: str, work_dir: str, pyproject: PyprojectDict, ar
|
|
|
164
263
|
|
|
165
264
|
os.symlink(os.path.join("usr", "bin", pyproject["script"]), os.path.join(app_root, "AppRun"))
|
|
166
265
|
|
|
167
|
-
|
|
168
|
-
if pyproject["icon"].endswith(".png"):
|
|
169
|
-
shutil.copyfile(os.path.join(project_dir, pyproject["icon"]), os.path.join(app_root, os.path.basename(pyproject["icon"])))
|
|
170
|
-
else:
|
|
171
|
-
# If a Icon is not PNG, convert it
|
|
172
|
-
subprocess.run([get_image_magick_command(work_dir), os.path.join(project_dir, pyproject["icon"]), "PNG:" + os.path.join(app_root, os.path.basename(pyproject["icon"]))], check=True)
|
|
266
|
+
handle_icon(project_dir, work_dir, app_root, pyproject)
|
|
173
267
|
|
|
174
|
-
|
|
175
|
-
os.rename(os.path.join(app_root, os.path.basename(pyproject["icon"])), os.path.join(app_root, pyproject["rename-icon"]))
|
|
176
|
-
icon_name = pyproject["rename-icon"]
|
|
177
|
-
else:
|
|
178
|
-
icon_name = os.path.basename(pyproject["icon"])
|
|
179
|
-
|
|
180
|
-
shutil.copyfile(os.path.join(app_root, icon_name), os.path.join(app_root, ".DirIcon"))
|
|
181
|
-
|
|
182
|
-
if icon_name.endswith(".svg"):
|
|
183
|
-
icon_dir = os.path.join(app_root, "usr", "share", "icons", "hicolor", "scalable", "apps")
|
|
184
|
-
else:
|
|
185
|
-
icon_size = get_icon_size(work_dir, os.path.join(app_root, icon_name))
|
|
186
|
-
icon_dir = os.path.join(app_root, "usr", "share", "icons", "hicolor", f"{icon_size[0]}x{icon_size[1]}", "apps")
|
|
187
|
-
|
|
188
|
-
if not os.path.isdir(icon_dir):
|
|
189
|
-
os.makedirs(icon_dir)
|
|
190
|
-
|
|
191
|
-
shutil.copyfile(os.path.join(app_root, icon_name), os.path.join(icon_dir, icon_name))
|
|
192
|
-
else:
|
|
193
|
-
shutil.copyfile(os.path.join(os.path.dirname(__file__), "default.png"), os.path.join(app_root, ".DirIcon"))
|
|
194
|
-
|
|
195
|
-
if "desktop-entry" in pyproject:
|
|
196
|
-
desktop_source_path = os.path.join(project_dir, pyproject["desktop-entry"])
|
|
197
|
-
desktop_dest_path = os.path.join(app_root, pyproject.get("rename-desktop-entry", os.path.basename(pyproject["desktop-entry"])))
|
|
198
|
-
|
|
199
|
-
if pyproject.get("gettext-desktop-entry", False):
|
|
200
|
-
subprocess.run(["msgfmt", "--desktop", "--template", desktop_source_path, "-d", os.path.join(project_dir, pyproject["gettext-directory"]), "-o", desktop_dest_path], check=True)
|
|
201
|
-
else:
|
|
202
|
-
shutil.copyfile(desktop_source_path, desktop_dest_path)
|
|
203
|
-
|
|
204
|
-
desktop_share_dir = os.path.join(app_root, "usr", "share", "applications")
|
|
205
|
-
|
|
206
|
-
if not os.path.isdir(desktop_share_dir):
|
|
207
|
-
os.makedirs(desktop_share_dir)
|
|
208
|
-
|
|
209
|
-
shutil.copyfile(desktop_dest_path, os.path.join(desktop_share_dir, os.path.basename(desktop_dest_path)))
|
|
210
|
-
else:
|
|
211
|
-
shutil.copyfile(os.path.join(os.path.dirname(__file__), "default.desktop"), os.path.join(app_root, "App.desktop"))
|
|
212
|
-
os.symlink(".DirIcon", os.path.join(app_root, "Icon.png"))
|
|
268
|
+
handle_desktop_entry(project_dir, app_root, pyproject)
|
|
213
269
|
|
|
214
270
|
if "appstream" in pyproject:
|
|
215
271
|
appstream_path = os.path.join(app_root, "usr", "share", "metainfo")
|
|
@@ -229,6 +285,9 @@ def build_appimage(project_dir: str, work_dir: str, pyproject: PyprojectDict, ar
|
|
|
229
285
|
else:
|
|
230
286
|
shutil.copyfile(appstream_source_path, appstream_dest_path)
|
|
231
287
|
|
|
288
|
+
if "additional-packages" in pyproject:
|
|
289
|
+
subprocess.run([os.path.join(app_root, "usr", "bin", "pip"), "install"] + pyproject["additional-packages"], check=True)
|
|
290
|
+
|
|
232
291
|
if args.output is not None:
|
|
233
292
|
output = args.output
|
|
234
293
|
elif "output" in pyproject:
|
|
@@ -285,7 +344,7 @@ def get_toml_section(data: dict[str, Any], name: str) -> Optional[dict[str, Any]
|
|
|
285
344
|
return current_data
|
|
286
345
|
|
|
287
346
|
|
|
288
|
-
def main():
|
|
347
|
+
def main() -> None:
|
|
289
348
|
parser = argparse.ArgumentParser()
|
|
290
349
|
parser.add_argument("--output", help="Sets the putput filename")
|
|
291
350
|
parser.add_argument("--project-dir", help="Sets the project dir", default=os.getcwd())
|
|
@@ -311,15 +370,7 @@ def main():
|
|
|
311
370
|
print(os.path.join(args.project_dir, "pyproject.toml") + " does not exists", file=sys.stderr)
|
|
312
371
|
sys.exit(1)
|
|
313
372
|
|
|
314
|
-
|
|
315
|
-
try:
|
|
316
|
-
data = toml_load(f)
|
|
317
|
-
except Exception as ex:
|
|
318
|
-
if len(ex.args) == 1:
|
|
319
|
-
print("Error while parsing " + os.path.join(args.project_dir, "pyproject.toml") + f": {ex.args[0]}", file=sys.stderr)
|
|
320
|
-
else:
|
|
321
|
-
print("Error while parsing " + os.path.join(args.project_dir, "pyproject.toml"), file=sys.stderr)
|
|
322
|
-
sys.exit(1)
|
|
373
|
+
data = read_pyproject_file(os.path.join(args.project_dir, "pyproject.toml"))
|
|
323
374
|
|
|
324
375
|
pyproject = get_toml_section(data, PYPROJECT_SECTION)
|
|
325
376
|
|
|
@@ -341,7 +392,7 @@ def main():
|
|
|
341
392
|
pass
|
|
342
393
|
|
|
343
394
|
try:
|
|
344
|
-
build_appimage(args.project_dir, args.work_dir, pyproject, args)
|
|
395
|
+
build_appimage(args.project_dir, os.path.abspath(args.work_dir), pyproject, args)
|
|
345
396
|
except subprocess.CalledProcessError as ex:
|
|
346
397
|
print("Error while running " + str(ex.cmd), file=sys.stderr)
|
|
347
398
|
sys.exit(1)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
4.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pyproject-appimage
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.0
|
|
4
4
|
Summary: Generate AppImages from your Python projects
|
|
5
5
|
Author-email: JakobDev <jakobdev@gmx.de>
|
|
6
6
|
License: BSD-2-Clause
|
|
@@ -25,6 +25,7 @@ Requires-Python: >=3.9
|
|
|
25
25
|
Description-Content-Type: text/markdown
|
|
26
26
|
License-File: LICENSE
|
|
27
27
|
Requires-Dist: requests
|
|
28
|
+
Requires-Dist: desktop-entry-lib
|
|
28
29
|
Requires-Dist: tomli; python_version < "3.11"
|
|
29
30
|
|
|
30
31
|
# pyproject-appimage
|
|
@@ -66,6 +67,7 @@ The following options can be used in your pyproject.toml:
|
|
|
66
67
|
| python-version | string | The Python version that is used. Default is your current version. Can be overwritten with the cli. |
|
|
67
68
|
| updateinformation | string | The [update information](https://github.com/AppImage/AppImageSpec/blob/master/draft.md#update-information)
|
|
68
69
|
| compression | string | The Squashfs compression
|
|
70
|
+
| additional-packages | list of strins | A list of packages that should also be installed
|
|
69
71
|
|
|
70
72
|
Note: All paths are relativ to your project directory
|
|
71
73
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
3.1
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyproject-appimage-3.1 → pyproject-appimage-4.0}/pyproject_appimage.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{pyproject-appimage-3.1 → pyproject-appimage-4.0}/pyproject_appimage.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|