|
@@ -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,33 +633,26 @@ 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):
|
|
@@ -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
|