#! /usr/bin/env python3 import argparse import sys import logging import subprocess import shutil import os from pathlib import Path logger = logging.getLogger(__name__) QMK_REPOSITORY = "https://github.com/qmk/qmk_firmware" QMK_PINNED_COMMIT = "daabe2d8c5eab9d9d605f8e079dfae82d2b06a8d" QMK_CLONE_PATH = Path("~/src/qmk_firmware").expanduser() QMK_USERNAME = "ewpratten" LOCAL_KEYMAPS_ROOT = Path(os.environ["EWCONFIG_ROOT"]) / "keyboards" / "qmk" / "keymaps" def check_prerequisite_tools() -> bool: # Ensure we have git if shutil.which("git") is None: logger.error("git is not installed") return False # Ensure we have make if shutil.which("make") is None: logger.error("make is not installed") return False # Ensure we have qmk if shutil.which("qmk") is None: logger.error("qmk is not installed") return False # OK return True def refresh_qmk_repo(): # If the repo doesn't exist, clone it if not QMK_CLONE_PATH.exists(): logger.info("Cloning QMK repository") QMK_CLONE_PATH.parent.mkdir(parents=True, exist_ok=True) subprocess.run( ["git", "clone", QMK_REPOSITORY, QMK_CLONE_PATH], check=True, ) # Drop any local changes logger.info("Dropping local changes") subprocess.run( ["git", "reset", "--hard"], check=True, cwd=QMK_CLONE_PATH, ) # Pull the latest changes and then checkout the pinned commit logger.info("Updating QMK repository") subprocess.run( ["git", "fetch", "--all"], check=True, cwd=QMK_CLONE_PATH, ) subprocess.run( ["git", "checkout", QMK_PINNED_COMMIT], check=True, cwd=QMK_CLONE_PATH, ) # Update submodules logger.info("Updating QMK submodules") subprocess.run( ["git", "submodule", "update", "--init", "--recursive"], check=True, cwd=QMK_CLONE_PATH, ) def copy_keymap(keyboard: str): # Build the path that this keymap should be copied to KEYMAP_PATH = QMK_CLONE_PATH / "keyboards" / keyboard / "keymaps" / QMK_USERNAME # If the keymap already exists, delete it if KEYMAP_PATH.exists(): logger.info("Removing existing keymap") shutil.rmtree(KEYMAP_PATH) # Copy the keymap logger.info(f"Copying keymap to: {KEYMAP_PATH}") shutil.copytree(LOCAL_KEYMAPS_ROOT / keyboard, KEYMAP_PATH) def build_keymap(keyboard: str): # Build the keymap logger.info(f"Building keymap: {keyboard}") subprocess.run( ["make", keyboard + ":" + QMK_USERNAME], check=True, cwd=QMK_CLONE_PATH, ) def flash_keymap(keyboard: str, flash_mode: str): # Flash the keymap logger.info(f"Flashing keymap: {keyboard} ({flash_mode})") subprocess.run( ["qmk", "flash", "-kb", keyboard, "-km", QMK_USERNAME, "-bl", flash_mode], check=True, cwd=QMK_CLONE_PATH, ) def main() -> int: # Handle program arguments ap = argparse.ArgumentParser( prog="qmk-helper", description="Utility for flashing QMK boards" ) ap.add_argument("mode", choices=["build", "flash"], help="Mode to run in") ap.add_argument( "keyboard", help="Keyboard to build/flash", choices=["tg4x", "ferris/sweep"], ) ap.add_argument("--flash-mode", "-f", help="Flash mode to use", default="flash") ap.add_argument( "-v", "--verbose", help="Enable verbose logging", action="store_true" ) args = ap.parse_args() # Configure logging logging.basicConfig( level=logging.DEBUG if args.verbose else logging.INFO, format="%(levelname)s: %(message)s", ) # Check for prerequisite tools if not check_prerequisite_tools(): return 1 logger.info("Prerequisite tools found") # Refresh the QMK repository refresh_qmk_repo() # Figure out the keymap name keymap = args.keyboard.split("/")[0] # Copy the keymap copy_keymap(keymap) # Handle the modes if args.mode == "build": build_keymap(args.keyboard) elif args.mode == "flash": # Make sure that the flash mode is valid if args.keyboard == "ferris/sweep" and args.flash_mode not in ["dfu-split-left", "dfu-split-right"]: logger.error( "Invalid flash mode. Must be one of: dfu-split-left, dfu-split-right" ) return 1 # Flash flash_keymap(args.keyboard, args.flash_mode) return 0 if __name__ == "__main__": sys.exit(main())