diff --git a/docker/build_tools.py b/docker/build_tools.py index 77d9c4e4..b9868b05 100644 --- a/docker/build_tools.py +++ b/docker/build_tools.py @@ -12,18 +12,27 @@ import io from os import walk from requests import Response -NEWLINE_CHAR = '\n' -def print_message(message,prefix=''): - trimmed=re.sub(r'\n', r'%0A', message,flags=re.MULTILINE) - print(f'{prefix}{trimmed}') - -def print_debug(message): - print_message(message,'::debug::') + +class Logger: + NEWLINE_CHAR = '\n' + @classmethod + def print_message(cls,message,prefix=''): + trimmed=re.sub(r'\n', r'%0A', message,flags=re.MULTILINE) + print(f'{prefix}{trimmed}') + @classmethod + def debug(cls,message): + cls.print_message(message,'::debug::') -def print_error(message): - print_message(message,'::error::') - + @classmethod + def error(cls,message): + cls.print_message(message,'::error::') + @classmethod + def notice(cls,message): + cls.print_message(message,'::notice::') + @classmethod + def warning(cls,message): + cls.print_message(message,'::notice::') try: @@ -34,7 +43,6 @@ try: import glob import json - import logging import re import shutil import stat @@ -60,12 +68,12 @@ try: from genericpath import isdir except ImportError as ex: - print_error( - f'Failed importing module {ex.name}, using interpreter {sys.executable}. {NEWLINE_CHAR} Installed packages:') + Logger.error( + f'Failed importing module {ex.name}, using interpreter {sys.executable}. {Logger.NEWLINE_CHAR} Installed packages:') installed_packages = pkg_resources.working_set installed_packages_list = sorted( ["%s==%s" % (i.key, i.version) for i in installed_packages]) - print(NEWLINE_CHAR.join(installed_packages_list)) + print(Logger.NEWLINE_CHAR.join(installed_packages_list)) print(f'Environment: ') envlist = "\n".join([f"{k}={v}" for k, v in sorted(os.environ.items())]) print(f'{envlist}') @@ -74,13 +82,15 @@ except ImportError as ex: tool_version = "1.0.7" WEB_INSTALLER_DEFAULT_PATH = './web_installer/' FORMAT = '%(asctime)s %(message)s' -logging.basicConfig(format=FORMAT) + github_env = type('', (), {})() manifest = { "name": "", "version": "", "home_assistant_domain": "slim_player", "funding_url": "https://esphome.io/guides/supporters.html", + "new_install_prompt_erase": True, + "new_install_improv_wait_time" : 20, "builds": [ { "chipFamily": "ESP32", @@ -277,21 +287,21 @@ class BinFile(): self.source_full_path = os.path.join( source_path, file_build_path).rstrip() self.offset = offset - self.target_name = f'{release_details.format_prefix()}-{self.name}'.rstrip() + self.target_name = f'{release_details.format_prefix()}-{release_details.bitrate}-{self.name}'.rstrip() def get_manifest(self): return {"path": self.target_name, "offset": self.offset} def copy(self, target_folder) -> str: self.target_fullpath = os.path.join(target_folder, self.target_name) - print_debug( + Logger.debug( f'File {self.source_full_path} will be copied to {self.target_fullpath}') try: os.makedirs(target_folder, exist_ok=True) shutil.copyfile(self.source_full_path, self.target_fullpath, follow_symlinks=True) except Exception as ex: - print_error(f"Error while copying {self.source_full_path} to {self.target_fullpath}{NEWLINE_CHAR}Content of {os.path.dirname(self.source_full_path.rstrip())}:{NEWLINE_CHAR}{NEWLINE_CHAR.join(get_file_list(os.path.dirname(self.source_full_path.rstrip())))}") + Logger.error(f"Error while copying {self.source_full_path} to {self.target_fullpath}{Logger.NEWLINE_CHAR}Content of {os.path.dirname(self.source_full_path.rstrip())}:{Logger.NEWLINE_CHAR}{Logger.NEWLINE_CHAR.join(get_file_list(os.path.dirname(self.source_full_path.rstrip())))}") @@ -323,7 +333,7 @@ class PlatformRelease(): flash_file_path: str def get_manifest_name(self) -> str: - return f'{self.name_prefix}-{self.release_details.format_prefix()}.json' + return f'{self.name_prefix}-{self.release_details.format_prefix()}-{self.release_details.bitrate}.json' def __init__(self, flash_file_path, git_release, build_dir, branch, name_prefix) -> None: self.name = git_release.tag_name @@ -389,8 +399,7 @@ class PlatformRelease(): self.has_artifacts = False def cleanup(self): - print( - f'removing temp directory for platform release {self.name}') + Logger.debug(f'removing temp directory for platform release {self.name}') shutil.rmtree(self.tempfolder) def get_attributes(self): @@ -438,7 +447,6 @@ class Releases(): return result def append(self, value: PlatformRelease): - # optional processing here if self.count(value) == 0: self._dict[value.platform()] = [] if self.should_add(value): @@ -465,8 +473,7 @@ class Releases(): def add_package(self, package: PlatformRelease, with_artifacts: bool = True): if self.branch != package.branch: - print( - f'Skipping release {package.name} from branch {package.branch}') + Logger.debug(f'Skipping release {package.name} from branch {package.branch}') elif package.has_artifacts or not with_artifacts: self.append(package) @@ -476,7 +483,7 @@ class Releases(): if last is None: return '' else: - return last.message.replace(NEWLINE_CHAR, ' ') + return last.message.replace(Logger.NEWLINE_CHAR, ' ') @classmethod def get_last_author(cls, repo_obj: Repository = None) -> Signature: @@ -505,7 +512,7 @@ class Releases(): print( f'Last commit for {head.shorthand} is {format_commit(cls.last_commit)}') except Exception as e: - print_error( + Logger.error( f'Unable to retrieve last commit for {head.shorthand}/{target}: {e}') cls.last_commit = None return cls.last_commit @@ -558,11 +565,11 @@ class Releases(): packages: Releases = cls(branch=repo.head.shorthand, maxcount=maxcount) build_dir = os.path.dirname(flash_file_path) for page in range(1, 999): - print_debug(f'Getting releases page {page}') + Logger.debug(f'Getting releases page {page}') releases = get_github_data( repo, f'releases?per_page=50&page={page}') if len(releases) == 0: - print_debug(f'No more release found for page {page}') + Logger.debug(f'No more release found for page {page}') break for release_entry in [AttributeDict(platform) for platform in releases]: packages.add_package(PlatformRelease(flash_file_path, release_entry, build_dir, @@ -588,14 +595,14 @@ class Releases(): break except Exception as e: - print_error( + Logger.error( f'Unable to get commit list starting at {last.id}: {e}') return commit_list @classmethod def get_commit_list_descriptions(cls) -> str: - return '<<~EOD\n### Revision Log\n'+NEWLINE_CHAR.join(cls.get_commit_list())+'\n~EOD' + return '<<~EOD\n### Revision Log\n'+Logger.NEWLINE_CHAR.join(cls.get_commit_list())+'\n~EOD' def update(self, *args, **kwargs): if args: @@ -626,34 +633,27 @@ def parse_json(filename: str): try: with open(fname) as f: content = f.read() - print_debug(f'Loading json\n{content}') + Logger.debug(f'Loading json\n{content}') return json.loads(content) except JSONDecodeError as ex: - print_error(f'Error parsing {content}') + Logger.error(f'Error parsing {content}') except Exception as ex: - print_error( - f"Unable to parse flasher args json file. Content of {folder}:{NEWLINE_CHAR.join(get_file_list(folder))}") + Logger.error( + f"Unable to parse flasher args json file. Content of {folder}:{Logger.NEWLINE_CHAR.join(get_file_list(folder))}") raise -def write_github_env(args): - print(f'Writing environment details to {args.env_file}...') - with open(args.env_file, "w") as env_file: - for attr in [attr for attr in dir(github_env) if not attr.startswith('_')]: - line = f'{attr}{"=" if attr != "description" else ""}{getattr(github_env,attr)}' +def write_github_env_file(values,env_file): + print(f'Writing content to {env_file}...') + with open(env_file, "w") as env_file: + for attr in [attr for attr in dir(values) if not attr.startswith('_')]: + line = f'{attr}{"=" if attr != "description" else ""}{getattr(values,attr)}' print(line) env_file.write(f'{line}\n') - os.environ[attr] = str(getattr(github_env, attr)) - print(f'Done writing environment details to {args.env_file}!') + os.environ[attr] = str(getattr(values, attr)) + print(f'Done writing to {env_file}!') -def set_workflow_output(args): - print(f'Outputting job variables ...') - for attr in [attr for attr in dir(github_env) if not attr.startswith('_')]: - print(f'::set-output name={attr}::{getattr(github_env,attr)}') - os.environ[attr] = str(getattr(github_env, attr)) - print(f'Done outputting job variables!') - def format_artifact_from_manifest(manif_json: AttributeDict): if len(manif_json) == 0: @@ -674,7 +674,19 @@ def handle_build_flags(args): github_env.release_flag = 1 if args.mock or args.force or 'release' in commit_message.lower() else 0 github_env.ui_build = 1 if args.mock or args.ui_build or '[ui-build]' in commit_message.lower( ) or github_env.release_flag == 1 else 0 - set_workflow_output(github_env) + write_github_env_file(github_env,os.environ.get('GITHUB_OUTPUT')) + +def write_version_number(file_path:str,env_details): + # app_name="${TARGET_BUILD_NAME}.${DEPTH}.dev-$(git log --pretty=format:'%h' --max-count=1).${branch_name}" + # echo "${app_name}">version.txt + try: + version:str = f'{env_details.TARGET_BUILD_NAME}.{env_details.DEPTH}.{env_details.major}.{env_details.BUILD_NUMBER}.{env_details.branch_name}' + with open(file_path, "w") as version_file: + version_file.write(version) + except Exception as ex: + Logger.error(f'Unable to set version string {version} in file {file_path}') + raise Exception('Version error') + Logger.notice(f'Firmware version set to {version}') def handle_environment(args): @@ -706,7 +718,8 @@ def handle_environment(args): github_env.artifact_bin_file_name = f"{github_env.artifact_prefix}.bin" github_env.PROJECT_VER = f'{args.node}-{ args.build }' github_env.description = Releases.get_commit_list_descriptions() - write_github_env(args) + write_github_env_file(github_env,args.env_file) + write_version_number("version.txt",github_env) def handle_artifacts(args): @@ -722,7 +735,7 @@ def handle_artifacts(args): os.makedirs(target_dir, exist_ok=True) shutil.copyfile(source, target, follow_symlinks=True) except Exception as ex: - print_error(f"Error while copying {source} to {target}\nContent of {target_dir}:\n{NEWLINE_CHAR.join(get_file_list(os.path.dirname(attr[0].rstrip())))}") + Logger.error(f"Error while copying {source} to {target}\nContent of {target_dir}:\n{Logger.NEWLINE_CHAR.join(get_file_list(os.path.dirname(attr[0].rstrip())))}") raise @@ -731,16 +744,16 @@ def delete_folder(path): for root, dirs, files in os.walk(path, topdown=True): for dir in dirs: fulldirpath = os.path.join(root, dir) - print_debug(f'Drilling down in {fulldirpath}') + Logger.debug(f'Drilling down in {fulldirpath}') delete_folder(fulldirpath) for fname in files: full_path = os.path.join(root, fname) - print_debug(f'Setting file read/write {full_path}') + Logger.debug(f'Setting file read/write {full_path}') os.chmod(full_path, stat.S_IWRITE) - print_debug(f'Deleting file {full_path}') + Logger.debug(f'Deleting file {full_path}') os.remove(full_path) if os.path.exists(path): - print_debug(f'Changing folder read/write {path}') + Logger.debug(f'Changing folder read/write {path}') os.chmod(path, stat.S_IWRITE) print(f'WARNING: Deleting Folder {path}') os.rmdir(path) @@ -797,7 +810,7 @@ def handle_manifest(args): man['builds'][0]['parts'] = release.process_files(args.outdir) man['name'] = release.platform() man['version'] = release.release_details.version - print_debug(f'Generated manifest: \n{json.dumps(man)}') + Logger.debug(f'Generated manifest: \n{json.dumps(man)}') fullpath = os.path.join(args.outdir, release.get_manifest_name()) print(f'Writing manifest to {fullpath}') with open(fullpath, "w") as f: @@ -827,7 +840,7 @@ def copy_no_overwrite(source: str, target: str): print(f'Copying {f} to target') shutil.copy(source_file, target_file) else: - print_debug(f'Skipping existing file {f}') + Logger.debug(f'Skipping existing file {f}') def get_changed_items(repo: Repository) -> Dict: @@ -870,13 +883,13 @@ def push_if_change(repo: Repository, token: str, source_path: str, manif_json): print( f'Pushing commit {format_commit(repo[commit])} to url {origin.url}') remote: Remote = repo.remotes['origin'] - auth_methods = ['x-access-token','x-oauth-basic'] - for method in auth_methods: - if push_with_method('x-access-token', token, remote, [reference]): - print(f'::notice Web installer updated for {format_artifact_from_manifest(manif_json)}') - return - - raise Exception('Unable to push web installer changes to installer repo') + # remote.credentials = credentials + auth_method = 'x-access-token' + remote.push([reference], callbacks=RemoteCallbacks( + pygit2.UserPass(auth_method, token))) + print( + f'::notice Web installer updated for {format_artifact_from_manifest(manif_json)}') + else: print(f'WARNING: No change found. Skipping update') @@ -921,8 +934,8 @@ def handle_show(args): def extract_files_from_archive(url): tempfolder = tempfile.mkdtemp() platform:Response = requests.get(url) - print_debug(f'Downloading {url} to {tempfolder}') - print_debug(f'Transfer status code: {platform.status_code}. Expanding content') + Logger.debug(f'Downloading {url} to {tempfolder}') + Logger.debug(f'Transfer status code: {platform.status_code}. Expanding content') z = zipfile.ZipFile(io.BytesIO(platform.content)) z.extractall(tempfolder) return tempfolder @@ -930,7 +943,7 @@ def extract_files_from_archive(url): def handle_list_files(args): print(f'Content of {args.cwd}:') - print(NEWLINE_CHAR.join(get_file_list(args.cwd))) + print(Logger.NEWLINE_CHAR.join(get_file_list(args.cwd))) parser_environment.set_defaults(func=handle_environment, cmd='environment') @@ -955,7 +968,7 @@ def main(): try: func(args) except Exception as e: - print_error(f'Critical error while running {args.command}\n{" ".join(traceback.format_exception(etype=type(e), value=e, tb=e.__traceback__))}') + Logger.error(f'Critical error while running {args.command}\n{" ".join(traceback.format_exception(etype=type(e), value=e, tb=e.__traceback__))}') exit_result_code = 1 else: # No subcommand was provided, so call help