import hashlib import shutil from pathlib import Path from urllib.request import urlretrieve import os import sys import json import re import requests from build import main as build_main def get_file_sha256(file_path): """Calculates the SHA256 hash of a file. Args: file_path: The path to the file. Returns: The SHA256 hash of the file as a hexadecimal string. """ sha256_hash = hashlib.sha256() try: with open(file_path, "rb") as f: for chunk in iter(lambda: f.read(4096), b""): sha256_hash.update(chunk) except FileNotFoundError: return "File not found." return sha256_hash.hexdigest() def print_hashes(hash_dict): for filename in sorted(hash_dict.keys()): print(f"{filename}: {hash_dict[filename]}") def is_release_required(): """ Checks if there are new versions of any apps. When new versions exist a new release is required. :return: True if there are new versions of any apps, and release is required, False otherwise. """ try: dl_dir = Path("latest_dl") if dl_dir.exists(): shutil.rmtree("latest_dl") dl_dir.mkdir() except FileExistsError: pass # download built zips from most recent release RELEASE_API_URL = "https://api.github.com/repos/adafruit/Fruit-Jam-OS/releases?per_page=1" latest_release_obj = requests.get(RELEASE_API_URL).json()[0] for asset in latest_release_obj["assets"]: asset_dl_url = asset["browser_download_url"] asset_filename = asset_dl_url.split("/")[-1] urlretrieve(asset_dl_url, f"latest_dl/{asset_filename}") # get sha256 hashes for each downloaded zip downloaded_file_hashes = {} for dl_file in Path("latest_dl").iterdir(): downloaded_file_hashes[dl_file.name] = get_file_sha256(dl_file) # make a local build build_main() # get sha256 hashes for built zips dist_file_hashes = {} for dist_file in Path("dist").iterdir(): dist_file_hashes[dist_file.name] = get_file_sha256(dist_file) print("Downloaded file hashes:") print_hashes(downloaded_file_hashes) print("Dist file hashes:") print_hashes(dist_file_hashes) # compare hashes if dist_file_hashes != downloaded_file_hashes: print("Zip hashes differ, a release is required.") return True print("Zip hashes match, no release required.") return False def parse_semantic_version(version_string): """Parse semantic version string and return (major, minor, patch).""" # Match semantic version pattern match = re.match(r'^(\d+)\.(\d+)\.(\d+)(?:-.*)?(?:\+.*)?$', version_string) if not match: raise ValueError(f"Invalid semantic version: {version_string}") return int(match.group(1)), int(match.group(2)), int(match.group(3)) def increment_patch_version(version_string): """Increment patch version by 1.""" major, minor, patch = parse_semantic_version(version_string) new_patch = patch + 1 return f"{major}.{minor}.{new_patch}" def get_latest_release(): """Fetch the latest release from GitHub API.""" url = f"https://api.github.com/repos/adafruit/Fruit-Jam-OS/releases/latest" headers = { "Authorization": f"token {os.getenv('GITHUB_TOKEN')}", "Accept": "application/vnd.github.v3+json" } response = requests.get(url, headers=headers) if response.status_code != 200: print(f"Error fetching latest release: {response.status_code}") print(f"Response: {response.text}") sys.exit(1) return response.json() def create_release(tag_name): """Create a new GitHub release.""" url = f"https://api.github.com/repos/adafruit/Fruit-Jam-OS/releases" headers = { "Authorization": f"token {os.getenv('GITHUB_TOKEN')}", "Accept": "application/vnd.github.v3+json", "Content-Type": "application/json" } data = { "tag_name": tag_name, "name": tag_name, "body": f"Release {tag_name}", "draft": False, "prerelease": False } response = requests.post(url, headers=headers, data=json.dumps(data)) if response.status_code != 201: print(f"Error creating release: {response.status_code}") print(f"Response: {response.text}") sys.exit(1) return response.json() if __name__ == '__main__': if is_release_required(): if len(sys.argv) > 1 and sys.argv[1] == "make_release": print(f"Creating release for Fruit Jam OS") # Get latest release latest_release = get_latest_release() if latest_release: latest_tag = latest_release["tag_name"] print(f"Latest release: {latest_tag}") try: new_tag = increment_patch_version(latest_tag) except ValueError as e: print(f"Error parsing version: {e}") sys.exit(1) else: new_tag = "0.1.0" print(f"Creating new release: {new_tag}") new_release = create_release( tag_name=new_tag, ) #print(new_release) github_output_path = os.environ.get('GITHUB_OUTPUT') if github_output_path: with open(github_output_path, 'a') as f: f.write(f"release_created=true\n") f.write(f"assets_upload_url={new_release['upload_url']}\n") print(f"Successfully created release: {new_tag}") print(f"Release URL: {new_release['html_url']}") else: github_output_path = os.environ.get('GITHUB_OUTPUT') if github_output_path: with open(github_output_path, 'a') as f: f.write(f"release_created=false\n") f.write(f"assets_upload_url=None\n")