From 2433a34364f632eb3b1cdb47092c624b57016a13 Mon Sep 17 00:00:00 2001 From: archie Date: Tue, 27 Feb 2024 13:13:46 +0000 Subject: [PATCH] Initial commit --- .gitignore | 1 + README.md | 3 + build/debug/file.elf | 1 + build/debug/file.hex | 1 + build/release/file.elf | 1 + build/release/file.hex | 0 changelogs/changelog_v0.1.md | 16 ++++ changelogs/changelog_v0.2.md | 7 ++ release-config.json | 22 +++++ release.py | 157 +++++++++++++++++++++++++++++++++++ 10 files changed, 209 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 build/debug/file.elf create mode 100644 build/debug/file.hex create mode 100644 build/release/file.elf create mode 100644 build/release/file.hex create mode 100644 changelogs/changelog_v0.1.md create mode 100644 changelogs/changelog_v0.2.md create mode 100644 release-config.json create mode 100644 release.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0cd3515 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +Releases diff --git a/README.md b/README.md new file mode 100644 index 0000000..f4ff6f4 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Auto-Firmware-Release + +This program aims to automate some of the annoying bits of making a firmware release. diff --git a/build/debug/file.elf b/build/debug/file.elf new file mode 100644 index 0000000..203ce03 --- /dev/null +++ b/build/debug/file.elf @@ -0,0 +1 @@ +hello, this is another file diff --git a/build/debug/file.hex b/build/debug/file.hex new file mode 100644 index 0000000..203ce03 --- /dev/null +++ b/build/debug/file.hex @@ -0,0 +1 @@ +hello, this is another file diff --git a/build/release/file.elf b/build/release/file.elf new file mode 100644 index 0000000..3a3424f --- /dev/null +++ b/build/release/file.elf @@ -0,0 +1 @@ +hello, this is a file diff --git a/build/release/file.hex b/build/release/file.hex new file mode 100644 index 0000000..e69de29 diff --git a/changelogs/changelog_v0.1.md b/changelogs/changelog_v0.1.md new file mode 100644 index 0000000..5ec307e --- /dev/null +++ b/changelogs/changelog_v0.1.md @@ -0,0 +1,16 @@ +# v0.1.3 + +* Fix what I hope is all the bugs + +# v0.1.2 + +* Fix another silly bug + +# v0.1.1 + +* Fix some stupid bugs + +# v0.1.0 + +Initial version + diff --git a/changelogs/changelog_v0.2.md b/changelogs/changelog_v0.2.md new file mode 100644 index 0000000..2793765 --- /dev/null +++ b/changelogs/changelog_v0.2.md @@ -0,0 +1,7 @@ +# v0.2.1 + +* Fix the feature + +# v0.2.0 + +* Implement crazy new feature diff --git a/release-config.json b/release-config.json new file mode 100644 index 0000000..7e2acc1 --- /dev/null +++ b/release-config.json @@ -0,0 +1,22 @@ + { + "product_name" : "Product_Name", + "gsd" : "999999", + "releases_directory": "Releases", + "changelog":"changelogs/", + "releases" : [ + { + "release_name": "Release", + "description": "Release Build", + "build_directory" : "build/release", + "build_file_name" : "file", + "copy_extensions" : [ "hex", "elf" ] + }, + { + "release_name": "Debug", + "description": "Debugging Build", + "build_directory" : "build/debug", + "build_file_name" : "file", + "copy_extensions" : [ "hex", "elf" ] + } + ] +} diff --git a/release.py b/release.py new file mode 100644 index 0000000..3c786d0 --- /dev/null +++ b/release.py @@ -0,0 +1,157 @@ +#!/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')