Skip to content

Commit 08ad6fc

Browse files
Paolo Calaopolldo
Paolo Calao
authored andcommitted
Generate and upload binaries on aws (#70)
This is the first step to decouple actual provisioning binaries from this repo The provisioning sketches have been introduced in the repo: whenever a change is made on the sketches, the CI will regenerate and upload the new binaries on aws.
1 parent 6493b0a commit 08ad6fc

File tree

8 files changed

+1007
-1
lines changed

8 files changed

+1007
-1
lines changed
+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
name: Sync bins
2+
on:
3+
workflow_dispatch:
4+
push:
5+
paths:
6+
- "firmware/**"
7+
- ".github/workflows/sync-binaries-task.yml"
8+
branches:
9+
- main
10+
11+
jobs:
12+
sync-binaries:
13+
runs-on: ubuntu-latest
14+
15+
steps:
16+
- name: Checkout repository
17+
uses: actions/checkout@v1
18+
19+
- name: Install Arduino CLI
20+
uses: arduino/setup-arduino-cli@v1
21+
22+
- name: Install Arduino CLI cores and libs for provisioning
23+
run: |
24+
arduino-cli core update-index -v
25+
arduino-cli version
26+
arduino-cli core install arduino:samd
27+
arduino-cli core install arduino:mbed_nano
28+
arduino-cli core install arduino:mbed_portenta
29+
arduino-cli lib install ArduinoIotCloud
30+
arduino-cli lib install ArduinoECCX08
31+
arduino-cli lib install ArduinoSTL
32+
arduino-cli lib install uCRC16Lib
33+
arduino-cli lib install Arduino_Portenta_OTA
34+
arduino-cli lib install MKRWAN
35+
36+
- name: Install Python
37+
uses: actions/setup-python@v2
38+
with:
39+
python-version: "3.9"
40+
41+
- name: Generate binaries and index
42+
run: |
43+
./firmware/generator.py
44+
45+
# fix `gpg: signing failed: Inappropriate ioctl for device`
46+
# https://github.com/keybase/keybase-issues/issues/2798
47+
- name: Import GPG key
48+
run: |
49+
echo "${{ secrets.GPG_PRIVATE_KEY }}" | base64 -di > /tmp/private.key
50+
gpg --batch --import --passphrase "${{ secrets.PASSPHRASE }}" /tmp/private.key
51+
echo "GPG_TTY=$(tty)" >> $GITHUB_ENV
52+
53+
# disable gpg pass prompt
54+
# https://stackoverflow.com/questions/49072403/suppress-the-passphrase-prompt-in-gpg-command
55+
- name: sign the json
56+
run: gpg --pinentry-mode=loopback --passphrase "${{ secrets.PASSPHRASE }}" --output firmware/binaries/index.json.sig --detach-sign firmware/binaries/index.json
57+
58+
- name: create the gzip
59+
run: gzip --keep firmware/binaries/index.json
60+
61+
- name: Upload binaries and index on S3
62+
run: |
63+
aws s3 sync ./firmware/binaries s3://cloud-downloads.arduino.cc/binaries
64+
env:
65+
AWS_REGION: "us-east-1"
66+
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
67+
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,7 @@ arduino-cloud-cli.exe
44

55
# Configuration file
66
config.yaml
7+
8+
# Provisioning binaries and metadata
9+
firmware/binaries
10+
firmware/provision/**/build

firmware/generator.py

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#!/usr/bin/env python3
2+
3+
import os
4+
import shutil
5+
import json
6+
import hashlib
7+
import sys
8+
from pathlib import Path
9+
import argparse
10+
import subprocess
11+
12+
DOWNLOAD_URL = "https://cloud-downloads.arduino.cc"
13+
14+
PROVISION_BINARY_PATHS = {
15+
"lora": "binaries/provision/lora",
16+
"crypto": "binaries/provision/crypto",
17+
}
18+
19+
SKETCH_NAMES = {
20+
"lora": "LoraProvision",
21+
"crypto": "CryptoProvision"
22+
}
23+
24+
INDEX_PATH = "binaries/index.json"
25+
26+
BOARDS = [
27+
{"type": "crypto", "ext": ".bin", "fqbn": "arduino:samd:nano_33_iot"},
28+
{"type": "crypto", "ext": ".bin", "fqbn": "arduino:samd:mkrwifi1010"},
29+
{"type": "crypto", "ext": ".elf", "fqbn": "arduino:mbed_nano:nanorp2040connect"},
30+
{"type": "crypto", "ext": ".bin", "fqbn": "arduino:mbed_portenta:envie_m7"},
31+
{"type": "crypto", "ext": ".bin", "fqbn": "arduino:samd:mkr1000"},
32+
{"type": "crypto", "ext": ".bin", "fqbn": "arduino:samd:mkrgsm1400"},
33+
{"type": "crypto", "ext": ".bin", "fqbn": "arduino:samd:mkrnb1500"},
34+
{"type": "lora", "ext": ".bin", "fqbn": "arduino:samd:mkrwan1300"},
35+
{"type": "lora", "ext": ".bin", "fqbn": "arduino:samd:mkrwan1310"},
36+
]
37+
38+
# Generates file SHA256
39+
def sha2(file_path):
40+
with open(file_path, "rb") as f:
41+
return hashlib.sha256(f.read()).hexdigest()
42+
43+
# Runs arduino-cli
44+
def arduino_cli(cli_path, args=None):
45+
if args is None:
46+
args=[]
47+
res = subprocess.run([cli_path, *args], capture_output=True, text=True, check=True)
48+
return res.stdout, res.stderr
49+
50+
def provision_binary_details(board):
51+
bin_path = PROVISION_BINARY_PATHS[board["type"]]
52+
simple_fqbn = board["fqbn"].replace(":", ".")
53+
sketch_dir = Path(__file__).parent / bin_path / simple_fqbn
54+
sketch_files = list(sketch_dir.iterdir())
55+
# there should be only one binary file
56+
if len(sketch_files) != 1:
57+
print(f"Invalid binaries found in {sketch_dir}")
58+
sys.exit(1)
59+
sketch_file = sketch_files[0]
60+
61+
sketch_dest = f"{bin_path}/{simple_fqbn}/{sketch_file.name}"
62+
file_hash = sha2(sketch_file)
63+
64+
return {
65+
"url": f"{DOWNLOAD_URL}/{sketch_dest}",
66+
"checksum": f"SHA-256:{file_hash}",
67+
"size": f"{sketch_file.stat().st_size}",
68+
}
69+
70+
def generate_index(boards):
71+
index_json = {"boards": []}
72+
for board in boards:
73+
index_board = {"fqbn": board["fqbn"]}
74+
index_board["provision"] = provision_binary_details(board)
75+
index_json["boards"].append(index_board)
76+
77+
p = Path(__file__).parent / INDEX_PATH
78+
with open(p, "w") as f:
79+
json.dump(index_json, f, indent=2)
80+
81+
def generate_binaries(arduino_cli_path, boards):
82+
for board in boards:
83+
sketch_path = Path(__file__).parent / "provision" / SKETCH_NAMES[board["type"]]
84+
print(f"Compiling for {board['fqbn']}")
85+
res, err = arduino_cli(arduino_cli_path, args=[
86+
"compile",
87+
"-e",
88+
"-b", board["fqbn"],
89+
sketch_path,
90+
])
91+
print(res, err)
92+
simple_fqbn = board["fqbn"].replace(":", ".")
93+
# Make output directory
94+
out = Path(__file__).parent / PROVISION_BINARY_PATHS[board["type"]] / simple_fqbn
95+
os.makedirs(out, exist_ok=True)
96+
# Copy the new binary file in the correct output directory
97+
compiled_bin = sketch_path / "build" / simple_fqbn / (SKETCH_NAMES[board["type"]] + ".ino" + board["ext"])
98+
shutil.copy2(compiled_bin, out / ("provision" + board["ext"]))
99+
100+
if __name__ == "__main__":
101+
parser = argparse.ArgumentParser(prog="generator.py")
102+
parser.add_argument(
103+
"-a",
104+
"--arduino-cli",
105+
default="arduino-cli",
106+
help="Path to arduino-cli executable",
107+
)
108+
args = parser.parse_args(sys.argv[1:])
109+
generate_binaries(args.arduino_cli, BOARDS)
110+
generate_index(BOARDS)

0 commit comments

Comments
 (0)