Compare commits

...

4 Commits

Author SHA1 Message Date
SantaSpeen 315b38a150 [?] Ready 2025-03-24 02:01:21 +03:00
SantaSpeen e98e5201b2 [+] poetry 2025-03-24 02:01:13 +03:00
SantaSpeen 957ca32639 [-] poetry.lock 2025-03-24 02:00:00 +03:00
SantaSpeen 8b35dd4f50 .idea 2025-03-24 01:59:52 +03:00
16 changed files with 335 additions and 0 deletions
+1
View File
@@ -168,3 +168,4 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
poetry.lock
+3
View File
@@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="Poetry (DistScripts)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
+28
View File
@@ -0,0 +1,28 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PyAssignmentToLoopOrWithParameterInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="PyClassHasNoInitInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="PyComparisonWithNoneInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="PyCompatibilityInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ourVersions">
<value>
<list size="2">
<item index="0" class="java.lang.String" itemvalue="3.12" />
<item index="1" class="java.lang.String" itemvalue="3.13" />
</list>
</value>
</option>
</inspection_tool>
<inspection_tool class="PyDeprecationInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PyDictDuplicateKeysInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PyListCreationInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="PyNonAsciiCharInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PyOverridesInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PyRedeclarationInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PyStatementEffectInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PyStringFormatInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PyTrailingSemicolonInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="t" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
</profile>
</component>
+6
View File
@@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.13" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Poetry (DistScripts)" project-jdk-type="Python SDK" />
</project>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/DistScripts.iml" filepath="$PROJECT_DIR$/.idea/DistScripts.iml" />
</modules>
</component>
</project>
Generated
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>
+1
View File
@@ -0,0 +1 @@
from .build_packet import build
+134
View File
@@ -0,0 +1,134 @@
import glob
import hashlib
import json
import shutil
import subprocess
from .patchers import patch_core_build
from .patchers import patch_metadata
from .config import *
product_name = "None" # Автоматически берется из metadata
def get_pyinstaller_cmd():
pyinstaller_cmd = \
(f'pyinstaller --noconfirm --onedir --console --clean '
f'--icon {path_fix + icon} --version-file {path_fix + metadata_path_txt} --name {product_name}'
f'{"".join([f' --add-data {path_fix + d}' for d in data])} '
f'--workpath {workpath} --distpath {distpath} --specpath {specpath} '
f'--contents-directory {contents_directory} --optimize {optimize} '
f'{"--disable-windowed-traceback " if disable_windowed_traceback else ""}'
f'{"--uac-admin " if admin else ""}'
f'{main}')
logger.info(f"execute: {pyinstaller_cmd}")
return pyinstaller_cmd.split(" ")
def calculate_sha256(file_path):
sha256_hash = hashlib.sha256()
with open(file_path, "rb") as f:
for byte_block in iter(lambda: f.read(4096), b""):
sha256_hash.update(byte_block)
return sha256_hash.hexdigest()
# Функция для получения списка файлов с их хешами
def generate_file_list_with_sha256(directory):
file_list = {}
for root, dirs, files in os.walk(directory):
for file in files:
file_path = os.path.join(root, file)
file_sha256 = calculate_sha256(file_path)
file_list[file_path] = (file_sha256, os.path.getsize(file_path))
return file_list
# Функция для нахождения различий
def find_differences(old, new):
diff = {}
# Найдем файлы, которые есть в одном списке, но не в другом
for file_path, (sha256, size) in old.items():
if file_path not in new:
diff[file_path] = ('deleted', sha256)
elif sha256 != new[file_path][0]:
diff[file_path] = ('updated', sha256)
for file_path, (sha256, size) in new.items():
if file_path not in old:
diff[file_path] = ('new_file', sha256)
return diff
def save_sha256(file_list, path:str=sha_file):
with open(path, 'w') as file:
# noinspection PyTypeChecker
json.dump(file_list, file, indent=4)
def read_sha256():
with open(sha_file, 'r') as file:
return json.load(file)
def prepare_dist():
for f in glob.glob(os.path.join(distpath, "warn*.txt")):
os.remove(f)
new_shas = generate_file_list_with_sha256(distpath)
if not sha_file.exists():
save_sha256(new_shas)
return new_shas
def generate_patch(old_ver, new_ver, diff):
update_dir = patch_dir.format(old_ver=old_ver, new_ver=new_ver)
os.makedirs(update_dir, exist_ok=True)
for file_path, (status, _) in diff.copy().items():
rel_path = os.path.relpath(file_path, distpath + product_name) # Убираем лишнюю часть пути
del diff[file_path] # Удаляем из списка файлов
diff.update({rel_path: status}) # Добавляем новый путь
if status == 'deleted':
continue
target_path = os.path.join(update_dir, rel_path) # Куда копировать
os.makedirs(os.path.dirname(target_path), exist_ok=True) # Создаем папку, если её нет
shutil.copy(file_path, target_path)
save_sha256(diff, patch_file.format(old_ver=old_ver, new_ver=new_ver))
return update_dir
def zip_latest(version):
shutil.make_archive(f"{dist_dir}/{product_name}-{version}", 'zip', latest_dir)
def update_latest():
shutil.rmtree(latest_dir)
shutil.copytree(distpath + product_name, latest_dir)
def cleanup():
shutil.rmtree(build_dir)
os.makedirs(build_dir, exist_ok=True)
def build():
global product_name
new_ver = patch_core_build()
old_ver, product_name = patch_metadata(*new_ver)
logger.info("Building...")
# subprocess.run(['auto-py-to-exe', '--config', build_json_path], shell=True)
subprocess.run(get_pyinstaller_cmd())
if not os.path.exists(distpath):
logger.info("[ERR] Build unsuccessful")
return
logger.info("Build successful")
logger.info("Preparing dist")
logger.info(" - generating sha256")
new_sha = prepare_dist()
old_sha = read_sha256()
logger.info(" - comparing..")
diff = find_differences(old_sha, new_sha)
logger.info(f" - {len(diff)} differences found")
new_ver = f"{new_ver[0]}.{new_ver[1]}.{new_ver[2]}.{new_ver[3]}"
if diff:
update_dir = generate_patch(old_ver, new_ver, diff)
logger.info(f" - diffs in: {update_dir.split("/")[-1]}")
logger.info(" - saving..")
save_sha256(new_sha)
update_latest()
zip_latest(new_ver)
logger.info(" - cleaning up")
cleanup()
logger.info("Ready")
shutil.copy(log_file, log_dir / f"build_{new_ver}.log")
+52
View File
@@ -0,0 +1,52 @@
import os
import sys
from pathlib import Path
from loguru import logger
# Пути к файлам
core_path = './src/core/core.py'
metadata_path = './win/metadata.yml'
metadata_path_txt = './win/version_file.txt'
build_json_path = './win/build.json'
# Настройки сборки
main = 'src/main.py'
icon = "./src/resources/ico/icon_dark.ico"
data = [
"./src/resources;resources/", # Папка с ресурсами для UI и т.д.
"./.venv/Lib/site-packages/customtkinter;customtkinter/", # Папка с библиотекой customtkinter
]
path_fix = os.path.abspath(os.path.dirname(__file__)) + "/../../"
contents_directory= "."
optimize=2
disable_windowed_traceback=True
admin=False
build_dir = 'win/build'
os.makedirs(build_dir, exist_ok=True)
distpath = f'{build_dir}/dist/'
workpath = f'{build_dir}/build/'
specpath = f'{build_dir}'
win_dir = Path('win')
dist_dir = win_dir / 'output'
sha_file = dist_dir / 'latest.json'
latest_dir = dist_dir / 'latest'
patch_dir = 'win/output/Patch {old_ver}-{new_ver}'
patch_file = 'win/output/Patch {old_ver}-{new_ver}/patch.json'
log_dir = win_dir / 'logs'
log_file = log_dir / 'latest.log'
os.makedirs(log_dir, exist_ok=True)
os.remove(log_file) if log_file.exists() else None
logger.remove()
fmt = "<green>{elapsed}</green> {message}"
logger.add(sys.stdout, level="INFO", format=fmt)
logger.add(log_file, level="INFO", format=fmt)
os.makedirs(latest_dir, exist_ok=True)
+2
View File
@@ -0,0 +1,2 @@
from .core_build import patch_core_build
from .metadata import patch_metadata
+31
View File
@@ -0,0 +1,31 @@
from ..config import *
def patch_core_build():
logger.info("Patching core")
with open(core_path, 'r', encoding="utf-8") as file:
core = file.read()
logger.info(" - core loaded")
logger.info(" - unpack values...")
ver_data_index = core.find("'", core.find("__version__ = "))
major, minor, patch = core[ver_data_index + 1:core.find("'", ver_data_index + 1)].split('.')
build_index_start = core.find("__build__ = ")
build_index_stop = core.find("\n", build_index_start)
build_i = int(core[build_index_start + 12:build_index_stop])
logger.info(f" - current version: {major}.{minor}.{patch}.{build_i}")
build_i += 1
logger.info(f" - patched version: {major}.{minor}.{patch}.{build_i}")
logger.info(" - patching")
core = core[:build_index_start] + f"__build__ = {build_i}" + core[build_index_stop:]
with open(core_path, 'w', encoding="utf-8") as file:
file.write(core)
logger.info(" - saved")
logger.info("Ready")
return major, minor, patch, build_i
if __name__ == '__main__':
patch_core_build()
+26
View File
@@ -0,0 +1,26 @@
import subprocess
from ruamel.yaml import YAML
from loguru import logger
from ..config import metadata_path, metadata_path_txt
yaml = YAML()
def patch_metadata(major, minor, patch, build_i):
logger.info("Patching metadata")
with open(metadata_path, 'r') as file:
metadata = yaml.load(file)
logger.info(" - metadata loaded")
product_name = metadata['ProductName']
old_data = metadata['Version']
logger.info(f" - current version: {old_data}")
logger.info(f" - patched version: {major}.{minor}.{patch}.{build_i}")
metadata['Version'] = f'{major}.{minor}.{patch}.{build_i}'
with open(metadata_path, 'w') as file:
yaml.dump(metadata, file)
logger.info(" - saved")
logger.info(" - creating version file")
subprocess.run(['create-version-file', metadata_path, '--outfile', metadata_path_txt])
logger.info("Ready")
return old_data, product_name
+4
View File
@@ -0,0 +1,4 @@
from dist_scripts import build
if __name__ == '__main__':
build()
+18
View File
@@ -0,0 +1,18 @@
[project]
name = "dist_scripts"
version = "1.0.0"
description = ""
authors = [
{name = "SantaSpeen",email = "santaspeen@gmail.com"}
]
readme = "README.md"
requires-python = ">=3.10,<4.0"
dependencies = [
"ruamel-yaml (>=0.18.10,<0.19.0)",
"loguru (>=0.7.3,<0.8.0)"
]
[build-system]
requires = ["poetry-core>=2.0.0,<3.0.0"]
build-backend = "poetry.core.masonry.api"