Skip to content

Commit 601f478

Browse files
committed
GH actions: Create Firmware zip file
1 parent dfb1c56 commit 601f478

File tree

3 files changed

+162
-3
lines changed

3 files changed

+162
-3
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Build Firmware ZIP
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
paths:
7+
- 'Station/Firmware**'
8+
- 'Station/zip_firmware.py'
9+
workflow_dispatch:
10+
11+
jobs:
12+
build:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- name: Checkout code
16+
uses: actions/checkout@v4
17+
with:
18+
fetch-depth: 1
19+
20+
- name: Set up Python
21+
uses: actions/setup-python@v5
22+
with:
23+
python-version: '3.x'
24+
25+
- name: Run firmware ZIP generator
26+
env:
27+
FIRMWARE_OUTPUT_DIR: ${{ github.workspace }}
28+
run: |
29+
python3 Station/zip_firmware.py
30+
31+
- name: Upload Firmware.zip
32+
uses: actions/upload-artifact@v4
33+
with:
34+
name: Firmware.zip
35+
path: ${{ github.workspace }}/Firmware.zip

Station/download_firmware.py

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Download and verify the latest Firmware.zip artifact from GitHub Actions
4+
for the WWCS project: https://github.com/WeatherWaterClimateServices/wwcs
5+
6+
Verification steps:
7+
- ZIP contains metadata.json
8+
- metadata.json has a 'gitversion' field (8-char hex string)
9+
- At least one file exists under a 'Firmware*' directory
10+
"""
11+
12+
import argparse
13+
import json
14+
import re
15+
import sys
16+
import tempfile
17+
import zipfile
18+
from pathlib import Path
19+
from urllib.error import URLError
20+
from urllib.request import urlopen
21+
22+
23+
URL = "https://api.github.com/repos/WeatherWaterClimateServices/wwcs"
24+
WORKFLOW_FILE = "build-firmware-zip.yml"
25+
26+
def fetch_json(url: str) -> dict:
27+
with urlopen(url) as resp:
28+
return json.load(resp)
29+
30+
def download_and_verify_firmware(output_path: str = "Firmware.zip") -> None:
31+
print("🔍 Fetching latest successful workflow run...")
32+
33+
try:
34+
runs = fetch_json(f"{URL}/actions/workflows/{WORKFLOW_FILE}/runs?status=success&per_page=1")
35+
except URLError as e:
36+
print(f"❌ Failed to fetch workflow runs: {e}", file=sys.stderr)
37+
sys.exit(1)
38+
39+
if not runs.get("workflow_runs"):
40+
print("❌ No successful workflow runs found.", file=sys.stderr)
41+
sys.exit(1)
42+
43+
run_id = runs["workflow_runs"][0]["id"]
44+
print(f"✅ Found run {run_id}")
45+
46+
try:
47+
artifacts = fetch_json(f"{URL}/actions/runs/{run_id}/artifacts")
48+
except URLError as e:
49+
print(f"❌ Failed to fetch artifacts: {e}", file=sys.stderr)
50+
sys.exit(1)
51+
52+
artifact = next((a for a in artifacts.get("artifacts", []) if a["name"] == "Firmware.zip"), None)
53+
if not artifact:
54+
print("❌ Firmware.zip artifact not found.", file=sys.stderr)
55+
sys.exit(1)
56+
57+
print("⬇️ Downloading Firmware.zip...")
58+
download_url = artifact["archive_download_url"]
59+
try:
60+
with urlopen(download_url) as resp:
61+
blob = resp.read()
62+
except URLError as e:
63+
print(f"❌ Download failed: {e}", file=sys.stderr)
64+
sys.exit(1)
65+
66+
# Write ZIP to disk
67+
zip_path = Path(output_path)
68+
zip_path.write_bytes(blob)
69+
print(f"💾 Saved to {zip_path}")
70+
71+
# === Verification ===
72+
print("🔍 Verifying contents...")
73+
74+
with tempfile.TemporaryDirectory() as tmpdir:
75+
tmp = Path(tmpdir)
76+
with zipfile.ZipFile(zip_path, "r") as zf:
77+
zf.extractall(tmp)
78+
79+
# 1. Check metadata.json exists
80+
metadata_file = tmp / "metadata.json"
81+
if not metadata_file.exists():
82+
print("❌ Verification failed: metadata.json not found in ZIP", file=sys.stderr)
83+
sys.exit(1)
84+
85+
# 2. Load and validate metadata
86+
try:
87+
metadata = json.loads(metadata_file.read_text())
88+
gitversion = metadata.get("gitversion")
89+
except (json.JSONDecodeError, OSError) as e:
90+
print(f"❌ Failed to read metadata.json: {e}", file=sys.stderr)
91+
sys.exit(1)
92+
93+
if not isinstance(gitversion, str) or not re.fullmatch(r"[a-f0-9]{8}", gitversion):
94+
print(f"❌ Invalid gitversion in metadata.json: {gitversion!r}", file=sys.stderr)
95+
sys.exit(1)
96+
97+
print(f"🔖 Git version: {gitversion}")
98+
99+
# 3. Check at least one Firmware* file exists
100+
firmware_files = list(tmp.glob("Firmware*/**/*"))
101+
firmware_files = [f for f in firmware_files if f.is_file()]
102+
103+
if not firmware_files:
104+
print("❌ Verification failed: no files found under Firmware*/", file=sys.stderr)
105+
sys.exit(1)
106+
107+
print(f"📦 Found {len(firmware_files)} firmware file(s) under Firmware*/")
108+
109+
print("✅ Verification passed!")
110+
print(f"✅ Firmware.zip is ready at {zip_path}")
111+
112+
def main() -> None:
113+
parser = argparse.ArgumentParser(
114+
description="Download and verify latest Firmware.zip from GitHub Actions (WWCS project)"
115+
)
116+
parser.add_argument(
117+
"-o", "--output",
118+
default="Firmware.zip",
119+
help="Output ZIP file path (default: Firmware.zip)"
120+
)
121+
args = parser.parse_args()
122+
download_and_verify_firmware(args.output)
123+
124+
if __name__ == "__main__":
125+
main()

Station/zip_firmware.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import argparse
1818
import json
19+
import os
1920
import pathlib
2021
import subprocess
2122
import zipfile
@@ -34,10 +35,8 @@ def zip_firmware():
3435
metadata = json.dumps(metadata)
3536

3637
# The output directory
37-
output_dir = '/var/www/html/downloads/Ij6iez6u'
38+
output_dir = os.environ.get('FIRMWARE_OUTPUT_DIR', '/tmp')
3839
output_dir = pathlib.Path(output_dir)
39-
if not output_dir.exists():
40-
output_dir = pathlib.Path('/tmp')
4140

4241
# Get the mtime of the zip file, if it exists.
4342
zip_path = output_dir / 'Firmware.zip'

0 commit comments

Comments
 (0)