Skip to content

Commit 89ff465

Browse files
authored
feat(esptool): Upgrade to esptool v5 (#11433)
* feat(esptool): Upgrade to esptool v5 * fix(script): Update script for better handling of esptool * fix(script): Get proper download url * fix(script): Apply copilot suggestions
1 parent e9813c6 commit 89ff465

File tree

3 files changed

+277
-41
lines changed

3 files changed

+277
-41
lines changed

.github/scripts/update_esptool.py

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
#!/usr/bin/env python3
2+
3+
# This script is used to re-package the esptool if needed and update the JSON file
4+
# for the Arduino ESP32 platform.
5+
#
6+
# The script has only been tested on macOS.
7+
#
8+
# For regular esptool releases, the generated packages already contain the correct permissions,
9+
# extensions and are uploaded to the GitHub release assets. In this case, the script will only
10+
# update the JSON file with the information from the GitHub release.
11+
#
12+
# The script can be used in two modes:
13+
# 1. Local build: The build artifacts must be already downloaded and extracted in the base_folder.
14+
# This is useful for esptool versions that are not yet released and that are grabbed from the
15+
# GitHub build artifacts.
16+
# 2. Release build: The script will get the release information from GitHub and update the JSON file.
17+
# This is useful for esptool versions that are already released and that are uploaded to the
18+
# GitHub release assets.
19+
#
20+
# For local build, the artifacts must be already downloaded and extracted in the base_folder
21+
# set with the -l option.
22+
# For example, a base folder "esptool" should contain the following folders extracted directly
23+
# from the GitHub build artifacts:
24+
# esptool/esptool-linux-aarch64
25+
# esptool/esptool-linux-amd64
26+
# esptool/esptool-linux-armv7
27+
# esptool/esptool-macos-amd64
28+
# esptool/esptool-macos-arm64
29+
# esptool/esptool-windows-amd64
30+
31+
import argparse
32+
import json
33+
import os
34+
import shutil
35+
import stat
36+
import tarfile
37+
import zipfile
38+
import hashlib
39+
import requests
40+
from pathlib import Path
41+
42+
def compute_sha256(filepath):
43+
sha256 = hashlib.sha256()
44+
with open(filepath, "rb") as f:
45+
for block in iter(lambda: f.read(4096), b""):
46+
sha256.update(block)
47+
return f"SHA-256:{sha256.hexdigest()}"
48+
49+
def get_file_size(filepath):
50+
return os.path.getsize(filepath)
51+
52+
def update_json_for_host(tmp_json_path, version, host, url, archiveFileName, checksum, size):
53+
with open(tmp_json_path) as f:
54+
data = json.load(f)
55+
56+
for pkg in data.get("packages", []):
57+
for tool in pkg.get("tools", []):
58+
if tool.get("name") == "esptool_py":
59+
tool["version"] = version
60+
61+
if url is None:
62+
# If the URL is not set, we need to find the old URL and update it
63+
for system in tool.get("systems", []):
64+
if system.get("host") == host:
65+
url = system.get("url").replace(system.get("archiveFileName"), archiveFileName)
66+
break
67+
else:
68+
print(f"No old URL found for host {host}. Using empty URL.")
69+
url = ""
70+
71+
# Preserve existing systems order and update or append the new system
72+
systems = tool.get("systems", [])
73+
system_updated = False
74+
for i, system in enumerate(systems):
75+
if system.get("host") == host:
76+
systems[i] = {
77+
"host": host,
78+
"url": url,
79+
"archiveFileName": archiveFileName,
80+
"checksum": checksum,
81+
"size": str(size),
82+
}
83+
system_updated = True
84+
break
85+
86+
if not system_updated:
87+
systems.append({
88+
"host": host,
89+
"url": url,
90+
"archiveFileName": archiveFileName,
91+
"checksum": checksum,
92+
"size": str(size),
93+
})
94+
tool["systems"] = systems
95+
96+
with open(tmp_json_path, "w") as f:
97+
json.dump(data, f, indent=2, sort_keys=False, ensure_ascii=False)
98+
f.write("\n")
99+
100+
def update_tools_dependencies(tmp_json_path, version):
101+
with open(tmp_json_path) as f:
102+
data = json.load(f)
103+
104+
for pkg in data.get("packages", []):
105+
for platform in pkg.get("platforms", []):
106+
for dep in platform.get("toolsDependencies", []):
107+
if dep.get("name") == "esptool_py":
108+
dep["version"] = version
109+
110+
with open(tmp_json_path, "w") as f:
111+
json.dump(data, f, indent=2, sort_keys=False, ensure_ascii=False)
112+
f.write("\n")
113+
114+
def create_archives(version, base_folder):
115+
archive_files = []
116+
117+
for dirpath in Path(base_folder).glob("esptool-*"):
118+
if not dirpath.is_dir():
119+
continue
120+
121+
base = dirpath.name[len("esptool-"):]
122+
123+
if "windows" in dirpath.name:
124+
zipfile_name = f"esptool-v{version}-{base}.zip"
125+
print(f"Creating {zipfile_name} from {dirpath} ...")
126+
with zipfile.ZipFile(zipfile_name, "w", zipfile.ZIP_DEFLATED) as zipf:
127+
for root, _, files in os.walk(dirpath):
128+
for file in files:
129+
full_path = os.path.join(root, file)
130+
zipf.write(full_path, os.path.relpath(full_path, start=dirpath))
131+
archive_files.append(zipfile_name)
132+
else:
133+
tarfile_name = f"esptool-v{version}-{base}.tar.gz"
134+
print(f"Creating {tarfile_name} from {dirpath} ...")
135+
for root, dirs, files in os.walk(dirpath):
136+
for name in dirs + files:
137+
os.chmod(os.path.join(root, name), stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR |
138+
stat.S_IRGRP | stat.S_IXGRP |
139+
stat.S_IROTH | stat.S_IXOTH)
140+
with tarfile.open(tarfile_name, "w:gz") as tar:
141+
tar.add(dirpath, arcname=dirpath.name)
142+
archive_files.append(tarfile_name)
143+
144+
return archive_files
145+
146+
def determine_hosts(archive_name):
147+
if "linux-amd64" in archive_name:
148+
return ["x86_64-pc-linux-gnu"]
149+
elif "linux-armv7" in archive_name:
150+
return ["arm-linux-gnueabihf"]
151+
elif "linux-aarch64" in archive_name:
152+
return ["aarch64-linux-gnu"]
153+
elif "macos-amd64" in archive_name:
154+
return ["x86_64-apple-darwin"]
155+
elif "macos-arm64" in archive_name:
156+
return ["arm64-apple-darwin"]
157+
elif "windows-amd64" in archive_name:
158+
return ["x86_64-mingw32", "i686-mingw32"]
159+
else:
160+
return []
161+
162+
def update_json_from_local_build(tmp_json_path, version, base_folder, archive_files):
163+
for archive in archive_files:
164+
print(f"Processing archive: {archive}")
165+
hosts = determine_hosts(archive)
166+
if not hosts:
167+
print(f"Skipping unknown archive type: {archive}")
168+
continue
169+
170+
archive_path = Path(archive)
171+
checksum = compute_sha256(archive_path)
172+
size = get_file_size(archive_path)
173+
174+
for host in hosts:
175+
update_json_for_host(tmp_json_path, version, host, None, archive_path.name, checksum, size)
176+
177+
def update_json_from_release(tmp_json_path, version, release_info):
178+
assets = release_info.get("assets", [])
179+
for asset in assets:
180+
if (asset.get("name").endswith(".tar.gz") or asset.get("name").endswith(".zip")) and "esptool" in asset.get("name"):
181+
asset_fname = asset.get("name")
182+
print(f"Processing asset: {asset_fname}")
183+
hosts = determine_hosts(asset_fname)
184+
if not hosts:
185+
print(f"Skipping unknown archive type: {asset_fname}")
186+
continue
187+
188+
asset_url = asset.get("browser_download_url")
189+
asset_checksum = asset.get("digest")
190+
asset_size = asset.get("size")
191+
if asset_checksum is None:
192+
asset_checksum = ""
193+
print(f"Asset {asset_fname} has no checksum. Please set the checksum in the JSON file.")
194+
195+
for host in hosts:
196+
update_json_for_host(tmp_json_path, version, host, asset_url, asset_fname, asset_checksum, asset_size)
197+
198+
def get_release_info(version):
199+
url = f"https://api.github.com/repos/espressif/esptool/releases/tags/v{version}"
200+
response = requests.get(url)
201+
response.raise_for_status()
202+
return response.json()
203+
204+
def main():
205+
parser = argparse.ArgumentParser(description="Repack esptool and update JSON metadata.")
206+
parser.add_argument("version", help="Version of the esptool (e.g. 5.0.dev1)")
207+
parser.add_argument("-l", "--local", dest="base_folder", help="Enable local build mode and set the base folder with unpacked artifacts")
208+
args = parser.parse_args()
209+
210+
script_dir = Path(__file__).resolve().parent
211+
json_path = (script_dir / "../../package/package_esp32_index.template.json").resolve()
212+
tmp_json_path = Path(str(json_path) + ".tmp")
213+
shutil.copy(json_path, tmp_json_path)
214+
215+
local_build = args.base_folder is not None
216+
217+
if local_build:
218+
os.chdir(args.base_folder)
219+
os.environ['COPYFILE_DISABLE'] = 'true' # this disables including resource forks in tar files on macOS
220+
# Clear any existing archive files
221+
for file in Path(args.base_folder).glob("esptool-*.*"):
222+
file.unlink()
223+
archive_files = create_archives(args.version, args.base_folder)
224+
update_json_from_local_build(tmp_json_path, args.version, args.base_folder, archive_files)
225+
else:
226+
release_info = get_release_info(args.version)
227+
update_json_from_release(tmp_json_path, args.version, release_info)
228+
229+
print(f"Updating esptool version fields to {args.version}")
230+
update_tools_dependencies(tmp_json_path, args.version)
231+
232+
shutil.move(tmp_json_path, json_path)
233+
print(f"Done. JSON updated at {json_path}")
234+
235+
if __name__ == "__main__":
236+
main()

package/package_esp32_index.template.json

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@
8181
{
8282
"packager": "esp32",
8383
"name": "esptool_py",
84-
"version": "4.9.dev3"
84+
"version": "5.0.dev1"
8585
},
8686
{
8787
"packager": "esp32",
@@ -469,56 +469,56 @@
469469
},
470470
{
471471
"name": "esptool_py",
472-
"version": "4.9.dev3",
472+
"version": "5.0.dev1",
473473
"systems": [
474474
{
475-
"host": "x86_64-pc-linux-gnu",
476-
"url": "https://github.com/espressif/arduino-esp32/releases/download/3.1.0-RC3/esptool-v4.9.dev3-linux-amd64.tar.gz",
477-
"archiveFileName": "esptool-v4.9.dev3-linux-amd64.tar.gz",
478-
"checksum": "SHA-256:4ecaf51836cbf4ea3c19840018bfef3b0b8cd8fc3c95f6e1e043ca5bbeab9bf0",
479-
"size": "64958202"
475+
"host": "aarch64-linux-gnu",
476+
"url": "https://github.com/espressif/arduino-esp32/releases/download/3.2.0/esptool-v5.0.dev1-linux-aarch64.tar.gz",
477+
"archiveFileName": "esptool-v5.0.dev1-linux-aarch64.tar.gz",
478+
"checksum": "SHA-256:bfafa7a7723ebbabfd8b6e3ca5ae00bfead0331de923754aeddb43b2c116a078",
479+
"size": "58241736"
480480
},
481481
{
482-
"host": "arm-linux-gnueabihf",
483-
"url": "https://github.com/espressif/arduino-esp32/releases/download/3.1.0-RC3/esptool-v4.9.dev3-linux-armv7.tar.gz",
484-
"archiveFileName": "esptool-v4.9.dev3-linux-armv7.tar.gz",
485-
"checksum": "SHA-256:fff818573bce483ee793ac83c8211f6abf764aa3350f198228859f696a0a0b36",
486-
"size": "31530030"
482+
"host": "x86_64-pc-linux-gnu",
483+
"url": "https://github.com/espressif/arduino-esp32/releases/download/3.2.0/esptool-v5.0.dev1-linux-amd64.tar.gz",
484+
"archiveFileName": "esptool-v5.0.dev1-linux-amd64.tar.gz",
485+
"checksum": "SHA-256:acd0486e96586b99d053a1479acbbbfcae8667227c831cdc53a171f9ccfa27ee",
486+
"size": "100740042"
487487
},
488488
{
489-
"host": "aarch64-linux-gnu",
490-
"url": "https://github.com/espressif/arduino-esp32/releases/download/3.1.0-RC3/esptool-v4.9.dev3-linux-aarch64.tar.gz",
491-
"archiveFileName": "esptool-v4.9.dev3-linux-aarch64.tar.gz",
492-
"checksum": "SHA-256:5b274bdff2f62e6a07c3c1dfa51b1128924621f661747eca3dbe0f77972f2f06",
493-
"size": "33663882"
489+
"host": "arm-linux-gnueabihf",
490+
"url": "https://github.com/espressif/arduino-esp32/releases/download/3.2.0/esptool-v5.0.dev1-linux-armv7.tar.gz",
491+
"archiveFileName": "esptool-v5.0.dev1-linux-armv7.tar.gz",
492+
"checksum": "SHA-256:ea77a38681506761bbb7b0b39c130811ed565667b67ebbdb4d6dcc6cb6e07368",
493+
"size": "53451939"
494494
},
495495
{
496496
"host": "x86_64-apple-darwin",
497-
"url": "https://github.com/espressif/arduino-esp32/releases/download/3.1.0-RC3/esptool-v4.9.dev3-macos-amd64.tar.gz",
498-
"archiveFileName": "esptool-v4.9.dev3-macos-amd64.tar.gz",
499-
"checksum": "SHA-256:c733c83b58fcf5f642fbb2fddb8ff24640c2c785126cba0821fb70c4a5ceea7a",
500-
"size": "32767836"
497+
"url": "https://github.com/espressif/arduino-esp32/releases/download/3.2.0/esptool-v5.0.dev1-macos-amd64.tar.gz",
498+
"archiveFileName": "esptool-v5.0.dev1-macos-amd64.tar.gz",
499+
"checksum": "SHA-256:900a8e90731208bee96647e0e207a43612b9452c2120c4fdc0ff4c6be226257b",
500+
"size": "59631998"
501501
},
502502
{
503503
"host": "arm64-apple-darwin",
504-
"url": "https://github.com/espressif/arduino-esp32/releases/download/3.1.0-RC3/esptool-v4.9.dev3-macos-arm64.tar.gz",
505-
"archiveFileName": "esptool-v4.9.dev3-macos-arm64.tar.gz",
506-
"checksum": "SHA-256:83c195a15981e6a5e7a130db2ccfb21e2d8093912e5b003681f9a5abadd71af7",
507-
"size": "30121441"
504+
"url": "https://github.com/espressif/arduino-esp32/releases/download/3.2.0/esptool-v5.0.dev1-macos-arm64.tar.gz",
505+
"archiveFileName": "esptool-v5.0.dev1-macos-arm64.tar.gz",
506+
"checksum": "SHA-256:3653f4de73cb4fc6a25351eaf663708e91c65ae3265d75bd54ca4315a4350bb4",
507+
"size": "56349992"
508508
},
509509
{
510-
"host": "i686-mingw32",
511-
"url": "https://github.com/espressif/arduino-esp32/releases/download/3.1.0-RC3/esptool-v4.9.dev3-win64.zip",
512-
"archiveFileName": "esptool-v4.9.dev3-win64.zip",
513-
"checksum": "SHA-256:890051a4fdc684ff6f4af18d0bb27d274ca940ee0eef716a9455f8c64b25b215",
514-
"size": "36072564"
510+
"host": "x86_64-mingw32",
511+
"url": "https://github.com/espressif/arduino-esp32/releases/download/3.2.0/esptool-v5.0.dev1-win64.zip",
512+
"archiveFileName": "esptool-v5.0.dev1-win64.zip",
513+
"checksum": "SHA-256:1e8fd89645daf94f2d4406ec73c9004e617ea921079515f9fd749205eece4d6d",
514+
"size": "59102658"
515515
},
516516
{
517-
"host": "x86_64-mingw32",
518-
"url": "https://github.com/espressif/arduino-esp32/releases/download/3.1.0-RC3/esptool-v4.9.dev3-win64.zip",
519-
"archiveFileName": "esptool-v4.9.dev3-win64.zip",
520-
"checksum": "SHA-256:890051a4fdc684ff6f4af18d0bb27d274ca940ee0eef716a9455f8c64b25b215",
521-
"size": "36072564"
517+
"host": "i686-mingw32",
518+
"url": "https://github.com/espressif/arduino-esp32/releases/download/3.2.0/esptool-v5.0.dev1-win64.zip",
519+
"archiveFileName": "esptool-v5.0.dev1-win64.zip",
520+
"checksum": "SHA-256:1e8fd89645daf94f2d4406ec73c9004e617ea921079515f9fd749205eece4d6d",
521+
"size": "59102658"
522522
}
523523
]
524524
},

0 commit comments

Comments
 (0)