Compare commits

..
Author SHA1 Message Date
ruv ef40b94b12 ci(submodules): reuse one rolling branch and PR instead of opening a new PR every 6 hours
The bot pushed a timestamped branch and opened a fresh PR on every scheduled
run, leaving 470+ superseded 'chore: update vendor submodules' PRs open. It now
rebuilds a single chore/update-submodules branch from main, force-pushes it,
and only opens a PR when none is open, so the existing PR refreshes in place.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-09-26 12:58:41 -04:00
14 changed files with 38 additions and 147 deletions

No files matched your search

+2 -2
View File
@@ -293,7 +293,7 @@ jobs:
done
- name: Update deployment status
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b
with:
script: |
const deployEnv = '${{ needs.pre-deployment.outputs.deploy_env }}';
@@ -344,7 +344,7 @@ jobs:
- name: Create deployment issue on failure
if: needs.deploy-production.result == 'failure'
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b
with:
script: |
github.rest.issues.create({
-32
View File
@@ -204,35 +204,3 @@ jobs:
idf.py build
test "$(stat -c%s build/esp32-csi-node.bin)" -le $((1152 * 1024))
strings build/esp32-csi-node.bin | grep -F "external U.FL"
publish-release-assets:
name: Publish firmware release assets
if: github.ref_type == 'tag'
needs: [version-guard, build, build-c6-idf6]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Download the exact-run firmware artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
with:
pattern: esp32-csi-node-firmware-*
path: release-downloads
- name: Package variants without filename collisions
run: |
mkdir -p release-assets
for directory in release-downloads/esp32-csi-node-firmware-*; do
variant="${directory##*/}"
tar -C "$directory" -czf "release-assets/${variant}.tar.gz" .
done
(cd release-assets && sha256sum *.tar.gz > SHA256SUMS.txt)
- name: Publish the downloadable firmware bundles
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65
with:
name: ESP32 firmware ${{ github.ref_name }}
body: |
Exact-run ESP32-S3 8 MB, ESP32-S3 4 MB, and ESP32-C6 4 MB firmware bundles.
Verify each archive against `SHA256SUMS.txt` before flashing.
files: release-assets/*
+1 -1
View File
@@ -460,7 +460,7 @@ jobs:
- name: Create security issue on critical findings
continue-on-error: true
if: needs.sast.result == 'failure' || needs.dependency-scan.result == 'failure'
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v6
uses: actions/github-script@00f12e3e20659f42342b1c0226afda7f7c042325 # v6
with:
script: |
github.rest.issues.create({
+17 -9
View File
@@ -50,18 +50,26 @@ jobs:
git diff --submodule=log -- vendor/ || true
fi
- name: Create PR with updates
# One rolling branch and at most one open PR. The branch is rebuilt from
# main on every run and force-pushed, so an open PR always shows the
# latest pointers instead of a new PR piling up every six hours.
- name: Create or refresh the update PR
if: steps.check.outputs.changed == 'true'
run: |
BRANCH="chore/update-submodules-$(date +%Y%m%d-%H%M%S)"
git checkout -b "$BRANCH"
BRANCH="chore/update-submodules"
git checkout -B "$BRANCH"
git add vendor/
git commit -m "chore: update vendor submodules to latest upstream"
git push origin "$BRANCH"
gh pr create \
--title "chore: update vendor submodules" \
--body "Automated submodule update to the latest upstream commit on each submodule's tracked branch (see \`.gitmodules\`). Review the pointer diff before merging." \
--base main \
--head "$BRANCH"
git push --force origin "$BRANCH"
OPEN=$(gh pr list --base main --head "$BRANCH" --state open --json number --jq '.[0].number')
if [ -n "$OPEN" ]; then
echo "Refreshed existing PR #$OPEN"
else
gh pr create \
--title "chore: update vendor submodules" \
--body "Automated submodule update to the latest upstream commit on each submodule's tracked branch (see \`.gitmodules\`). This PR is refreshed in place on every scheduled run. Review the pointer diff before merging." \
--base main \
--head "$BRANCH"
fi
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-8
View File
@@ -362,14 +362,6 @@ The `MSYS_NO_PATHCONV=1` prefix prevents Git Bash from mangling the `/project` p
- `build/partition_table/partition-table.bin` -- flash partition layout
- `build/esp32-csi-node.bin` -- application firmware
### Release bundles
Firmware downloads are published from dedicated `vX.Y.Z-esp32` tags after all
S3 and C6 matrix builds pass. Each release contains separate S3 8 MB, S3 4 MB,
and C6 4 MB archives plus an archive checksum manifest. The repository's
automated `vNNNN` server releases (for example `v2655`) are not firmware tags;
do not use their presence or absence of assets as firmware provenance.
### Custom Configuration
To change Kconfig settings before building:
-42
View File
@@ -19,14 +19,6 @@
#include "nvs_config.h"
#include "thermal.h"
/* firmware/esp32-csi-node/partitions_4mb.csv has two 1.81 MB app slots and
* only 256 KB free -- no room for the FAT volume this module needs. The
* whole implementation below is compiled out on that profile; every entry
* point still links via the stubs in the #else branch so main.c's call
* sites need no #ifdef of their own. See partitions_display.csv (8MB) and
* partitions_16mb.csv for the profiles where this module is active. */
#ifndef CONFIG_ESPTOOLPY_FLASHSIZE_4MB
static const char *TAG = "node_log";
/* The FAT volume reserved in partitions_16mb.csv (storage, 8000K) and never
@@ -388,37 +380,3 @@ esp_err_t node_log_clear(void)
xSemaphoreGive(s_lock);
return err;
}
#else /* CONFIG_ESPTOOLPY_FLASHSIZE_4MB */
esp_err_t node_log_init(void) { return ESP_ERR_NOT_SUPPORTED; }
bool node_log_is_active(void) { return false; }
void node_log_boot(uint32_t reset_reason, uint32_t prev_uptime_s)
{
(void)reset_reason;
(void)prev_uptime_s;
}
void node_log_periodic(void) { }
void node_log_event(uint8_t subtype, int32_t a, int32_t b)
{
(void)subtype;
(void)a;
(void)b;
}
void node_log_note_disconnect(uint8_t reason, int8_t rssi)
{
(void)reason;
(void)rssi;
}
size_t node_log_read(uint8_t *out, size_t max_records, size_t skip)
{
(void)out;
(void)max_records;
(void)skip;
return 0;
}
size_t node_log_count(void) { return 0; }
uint16_t node_log_boot_id(void) { return 0; }
esp_err_t node_log_clear(void) { return ESP_ERR_NOT_SUPPORTED; }
#endif /* CONFIG_ESPTOOLPY_FLASHSIZE_4MB */
@@ -162,9 +162,16 @@ static void serial_onboarding_task(void *argument)
(void)argument;
const int original_flags = fcntl(STDIN_FILENO, F_GETFL, 0);
if (original_flags >= 0) (void)fcntl(STDIN_FILENO, F_SETFL, original_flags | O_NONBLOCK);
/* ESP-IDF secondary consoles mirror output but deliberately do not forward
* input to STDIN. Native S3/C6 USB Serial/JTAG therefore needs a bounded
* direct VFS reader while UART-console boards continue to use STDIN. */
const int usb_fd = open("/dev/secondary", O_RDONLY | O_NONBLOCK);
char primary_line[ONBOARDING_LINE_MAX];
char usb_line[ONBOARDING_LINE_MAX];
size_t primary_length = 0;
size_t usb_length = 0;
bool primary_overflow = false;
bool usb_overflow = false;
for (;;) {
uint8_t input[64];
const ssize_t count = read(STDIN_FILENO, input, sizeof(input));
@@ -175,6 +182,12 @@ static void serial_onboarding_task(void *argument)
ESP_LOGW(TAG, "serial input unavailable: errno=%d", errno);
vTaskDelay(pdMS_TO_TICKS(500));
}
if (usb_fd >= 0) {
const ssize_t usb_count = read(usb_fd, input, sizeof(input));
if (usb_count > 0) {
consume_bytes(input, usb_count, usb_line, &usb_length, &usb_overflow);
}
}
vTaskDelay(pdMS_TO_TICKS(20));
}
}
@@ -6,4 +6,3 @@ phy_init, data, phy, 0x11000, 0x1000,
ota_0, app, ota_0, 0x20000, 0x200000,
ota_1, app, ota_1, 0x220000, 0x200000,
spiffs, data, spiffs, 0x420000, 0x1E0000,
storage, data, fat, 0x600000, 0x200000,
1 # ESP32-S3 CSI Node — 8MB flash partition table (ADR-045)
6 ota_0, app, ota_0, 0x20000, 0x200000,
7 ota_1, app, ota_1, 0x220000, 0x200000,
8 spiffs, data, spiffs, 0x420000, 0x1E0000,
storage, data, fat, 0x600000, 0x200000,
@@ -1,4 +1,4 @@
12e9fe5bd0533c3eb8ebb1cc36f775668cc4dd39f334ea65e26529b87211dc4c bootloader.bin
baff3940ede0ce1658f2f5c3ba458cc9007753eb1519231d67b1ffa724382824 esp32-csi-node.bin
126a4b9f305e207aa2e0db93caf09e27f77b3bee4b4f618a0cb963f10a4364bc bootloader.bin
183c4557a93f6f0be1680f45988b7c7320130c8e0ed5d9c900add038d5cc84fb esp32-csi-node.bin
7d2c7ac4888bfd75cd5f56e8d61f69595121183afc81556c876732fd3782c62f ota_data_initial.bin
4c2cc4ffd52641e23b779bd57b3908014083ac3c1aab395756478c89e70d81f0 partition-table.bin
@@ -19,12 +19,6 @@ CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_4mb.csv"
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_ESPTOOLPY_FLASHSIZE="4MB"
# Native USB/JTAG is the interactive onboarding console on C6 boards. ESP-IDF
# secondary consoles mirror output only and cannot receive RUVIEW_HELLO_V1.
CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y
# CONFIG_ESP_CONSOLE_UART_DEFAULT is not set
# CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG is not set
# ── CSI (required) ──
CONFIG_ESP_WIFI_CSI_ENABLED=y
+3 -7
View File
@@ -55,8 +55,7 @@ FUZZ_JOBS ?= 1
.PHONY: all clean run_serialize run_edge run_nvs run_all test_adr110 run_adr110 \
test_vitals run_vitals test_mmwave_detect run_mmwave_detect host_tests \
test_thermal run_thermal test_csi_sanitize run_csi_sanitize \
test_c6_antenna run_c6_antenna test_serial_onboarding run_serial_onboarding \
run_delivery_contract
test_c6_antenna run_c6_antenna test_serial_onboarding run_serial_onboarding
all: fuzz_serialize fuzz_edge fuzz_nvs test_adr110 test_vitals test_mmwave_detect \
test_thermal test_csi_sanitize test_c6_antenna test_serial_onboarding
@@ -133,12 +132,9 @@ test_serial_onboarding: test_serial_onboarding_protocol.c $(MAIN_DIR)/serial_onb
run_serial_onboarding: test_serial_onboarding
./test_serial_onboarding
run_delivery_contract:
python3 test_delivery_contract.py
host_tests: run_adr110 run_vitals run_mmwave_detect run_thermal run_csi_sanitize \
run_c6_antenna run_serial_onboarding run_delivery_contract
@echo "Host tests passed (ADR-110 + CSI sanitation + vitals + mmwave + thermal + C6 antenna + USB onboarding + delivery contract)"
run_c6_antenna run_serial_onboarding
@echo "Host tests passed (ADR-110 + CSI sanitation + vitals + mmwave + thermal + C6 antenna + USB onboarding)"
# --- Serialize fuzzer ---
# Tests csi_serialize_frame() with random wifi_csi_info_t inputs.
@@ -1,37 +0,0 @@
#!/usr/bin/env python3
"""Static delivery contracts that do not require an ESP-IDF toolchain."""
from pathlib import Path
import unittest
FIRMWARE_ROOT = Path(__file__).resolve().parents[1]
REPOSITORY_ROOT = FIRMWARE_ROOT.parents[1]
class FirmwareDeliveryContractTests(unittest.TestCase):
def test_c6_uses_native_usb_serial_jtag_as_primary_console(self) -> None:
config = (FIRMWARE_ROOT / "sdkconfig.defaults.esp32c6").read_text()
self.assertIn("CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y", config)
self.assertNotIn("CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG=y", config)
onboarding = (FIRMWARE_ROOT / "main/serial_onboarding.c").read_text()
self.assertNotIn('open("/dev/secondary"', onboarding)
def test_tracked_c6_onboarding_app_matches_version_txt(self) -> None:
expected = (FIRMWARE_ROOT / "version.txt").read_text().strip().encode()
app = (FIRMWARE_ROOT / "release_bins/c6-onboarding/esp32-csi-node.bin").read_bytes()
self.assertTrue(
expected + b"\0" in app,
f"tracked C6 app does not embed version {expected.decode()}",
)
def test_firmware_tags_publish_downloadable_release_assets(self) -> None:
workflow = (REPOSITORY_ROOT / ".github/workflows/firmware-ci.yml").read_text()
self.assertIn("publish-release-assets:", workflow)
self.assertIn("esp32-csi-node-firmware-*", workflow)
self.assertIn("softprops/action-gh-release@", workflow)
if __name__ == "__main__":
unittest.main()