158 lines
5.5 KiB
Python
158 lines
5.5 KiB
Python
#!/usr/bin/python3
|
|
import logging
|
|
import sys
|
|
import json
|
|
import argparse
|
|
import os
|
|
import hashlib
|
|
from shutil import copyfile
|
|
from datetime import datetime
|
|
|
|
logger = logging.getLogger()
|
|
handler = logging.StreamHandler(stream=sys.stderr)
|
|
formatter = logging.Formatter(
|
|
'%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
|
|
handler.setFormatter(formatter)
|
|
logger.addHandler(handler)
|
|
logger.setLevel(logging.DEBUG)
|
|
|
|
parser = argparse.ArgumentParser(
|
|
prog="mkrelease",
|
|
description="Automatically package a firmware release."
|
|
)
|
|
|
|
parser.add_argument('-c', '--config', dest="config_location", action="store", help="The location of the config file.", default="./release-config.json")
|
|
parser.add_argument('version', help="Version you wish to release. Use https://semver.org")
|
|
parser.add_argument('--verbose', help="Enable verbose logging output.", action="store_true", default=False)
|
|
|
|
def build_version_string(gsd_num : str, out_name:str, version_number:str, release_type:str, file_extension:str):
|
|
return f'GSD-{gsd_num}-{out_name}-{version_number}-{release_type}.{file_extension}'
|
|
|
|
def md5_hash_of(file):
|
|
with open(file, 'rb') as f:
|
|
file_hash = hashlib.md5()
|
|
while chunk := f.read(8192):
|
|
file_hash.update(chunk)
|
|
|
|
return file_hash.hexdigest()
|
|
|
|
if __name__ == "__main__":
|
|
args = parser.parse_args()
|
|
|
|
if not args.verbose:
|
|
logger.disabled = True
|
|
|
|
|
|
logger.debug("Attempting to read config file...")
|
|
|
|
config = None
|
|
|
|
try:
|
|
config = json.load(open(args.config_location))
|
|
except Exception as e:
|
|
logger.error(args.config_location + ": " + str(e))
|
|
|
|
assert config is not None
|
|
|
|
logger.debug("Config file read.")
|
|
|
|
release_dir = f'./{config["releases_directory"]}/{args.version}/'
|
|
|
|
print("The following releases will be created:")
|
|
|
|
assert config["releases"] is not None
|
|
|
|
missingfiles = []
|
|
|
|
binaries_manifest = []
|
|
|
|
print(release_dir)
|
|
for release in config["releases"]:
|
|
for ext in release["copy_extensions"]:
|
|
build_file_path = f'./{release["build_directory"]}/{release["build_file_name"]}.{ext}'
|
|
output_file_path = build_version_string(config["gsd"]
|
|
, config["product_name"]
|
|
, args.version
|
|
, release["release_name"]
|
|
, ext)
|
|
try:
|
|
os.stat(build_file_path)
|
|
except Exception as e:
|
|
logging.error(f"Could not stat {build_file_path}:")
|
|
logging.error(e)
|
|
missingfiles.append(build_file_path)
|
|
|
|
print('\t'+ output_file_path + f'\t({build_file_path})')
|
|
|
|
binaries_manifest.append((build_file_path, release_dir + output_file_path))
|
|
|
|
|
|
if len(missingfiles) > 0:
|
|
logger.error("Exiting due to missing source files.")
|
|
print("The following files were not able to be found:")
|
|
[print(f"\t{path}") for path in missingfiles]
|
|
exit(1)
|
|
|
|
print("\nThe following additional files will be created:")
|
|
print(f'./{config["releases_directory"]}/{args.version}/')
|
|
print("\trelease_notes.txt")
|
|
|
|
if config["changelog"] is not None:
|
|
logger.debug("Detected changelog field. Will also create changelog.")
|
|
if not os.path.exists(config["changelog"]):
|
|
logger.warning("Config file does not exist.")
|
|
print("The changelog file you provided does not exist. Check your configuration file.")
|
|
elif os.path.isfile(config["changelog"]):
|
|
print("\tchangelog.md (from file)")
|
|
elif os.path.isdir(config["changelog"]):
|
|
print("\tchangelog.md (from directory of files)")
|
|
|
|
print("")
|
|
|
|
accept_words = ["y", "yes", ""]
|
|
deny_words = ["n", "no"]
|
|
while True:
|
|
print("Do you wish to proceed? [Y/n]: ", end="")
|
|
result = input()
|
|
|
|
|
|
if result.lower() in deny_words:
|
|
print("Aborted.")
|
|
exit(1)
|
|
elif result.lower() not in accept_words:
|
|
print("Don't understand what you mean.")
|
|
else:
|
|
print("OK, executing file operations.")
|
|
break
|
|
|
|
logger.debug("Ensuring output directories exist.")
|
|
os.makedirs(release_dir, exist_ok=True)
|
|
|
|
for (source,destination) in binaries_manifest:
|
|
copyfile(source, destination)
|
|
logger.debug(f'Copied {source} to {destination}')
|
|
|
|
|
|
logger.debug(f'Generating changelog')
|
|
|
|
if os.path.isdir(config["changelog"]):
|
|
with open(release_dir + "changelog.md", mode='+w') as out_log:
|
|
for f in reversed(os.listdir(config["changelog"])):
|
|
if os.path.isfile(f'{config["changelog"]}/{f}'):
|
|
with open(f'{config["changelog"]}/{f}') as fi:
|
|
for line in fi:
|
|
out_log.write(line)
|
|
out_log.write("\n")
|
|
|
|
else:
|
|
copyfile(config["changelog"], release_dir + "/changelog.md")
|
|
logger.debug(f'Copied {config["changelog"]} to {release_dir}/changelog.txt')
|
|
|
|
logger.debug("Generating release_notes.txt")
|
|
|
|
with open(f'{release_dir}/release_notes.txt', mode="+w") as release_notes:
|
|
release_notes.write(f'Generated using mkrelease at {datetime.now().strftime("%Y/%m/%d %H:%M:%S")} by user {os.getlogin()}\n')
|
|
release_notes.write("Output file manifest (name, md5 hash):\n")
|
|
for (_, file) in binaries_manifest:
|
|
release_notes.write(f'{file} {md5_hash_of(file)}\n')
|