memfault_elf_uploader.py
1 #!/usr/bin/env python3 2 3 import os 4 import sys 5 import subprocess 6 import glob 7 import git 8 import argparse 9 10 from dotenv import load_dotenv, find_dotenv 11 12 RELEASE_BRANCH = "main" 13 14 15 def get_git_branch(): 16 git_repo = git.Repo(search_parent_directories=True) 17 return git_repo.git.branch("--show-current") 18 19 20 def get_project_root(): 21 git_repo = git.Repo(search_parent_directories=True) 22 return git_repo.git.rev_parse("--show-toplevel") 23 24 25 """ 26 Example readelf -n output: 27 28 Displaying notes found in: .note.gnu.build-id 29 Owner Data size Description 30 GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring) 31 Build ID: cebe50e6d5aa603cc53bb3d77f5177a287f42991 32 """ 33 34 35 def get_build_id(elf_file): 36 notes = subprocess.getoutput(f"arm-none-eabi-readelf -n {elf_file}") 37 38 # Return build id from the last line of the output 39 # TODO - actually search for "Build ID: xxxxxxxx" since this will break 40 # if we ever add another note after the build id 41 build_id = notes.split("\n")[-1].split(":")[-1].strip() 42 43 print(f'Using build id: "{build_id}"') 44 45 return build_id 46 47 48 def get_git_version(): 49 return subprocess.getoutput("git describe --always --abbrev=8 --dirty") 50 51 52 if __name__ == "__main__": 53 54 parser = argparse.ArgumentParser() 55 parser.add_argument("--software_type", default="unknown", help="Software type") 56 parser.add_argument("--project", default="bristlemouth", help="Memfault project name") 57 parser.add_argument("--org", default="sofar-ocean", help="Memfault org") 58 parser.add_argument("--version", help="Version string") 59 parser.add_argument("--username", help="Memfault username/email") 60 parser.add_argument("--password", help="Memfault password/api key") 61 parser.add_argument("--org-token", help="Memfault org token") 62 parser.add_argument("--verbose", action="store_true") 63 parser.add_argument("elf_file", help="Elf file to upload") 64 args = parser.parse_args() 65 66 load_dotenv(find_dotenv()) 67 68 elf_file = args.elf_file 69 70 if args.version is None: 71 version = get_git_version() 72 if get_git_branch() != RELEASE_BRANCH: 73 version = "ENG-" + version + "+" + get_build_id(elf_file)[:8] 74 else: 75 version = args.version 76 77 # Put together memfault command 78 memfault_cmd_args = [ 79 "memfault", 80 "--org", 81 args.org, 82 "--project", 83 args.project, 84 ] 85 86 # token or email/password need to precede the `upload-symbols` command 87 if os.getenv("MEMFAULT_ORG_TOKEN"): 88 memfault_cmd_args += [ 89 "--org-token", 90 os.getenv("MEMFAULT_ORG_TOKEN") 91 if args.org_token is None 92 else args.org_token, 93 ] 94 elif os.getenv("MEMFAULT_USERNAME") and os.getenv("MEMFAULT_PASSWORD"): 95 memfault_cmd_args += [ 96 "--email", 97 os.getenv("MEMFAULT_USERNAME") if args.username is None else args.username, 98 "--password", 99 os.getenv("MEMFAULT_PASSWORD") if args.password is None else args.password, 100 ] 101 else: 102 raise EnvironmentError("Memfault org token or username/password required!") 103 104 memfault_cmd_args += [ 105 "upload-symbols", 106 elf_file, 107 "--software-type", 108 args.software_type, 109 "--software-version", 110 version, 111 ] 112 113 if args.verbose: 114 print(" ".join(memfault_cmd_args)) 115 116 subprocess.run(memfault_cmd_args)