diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0e625ca..b07436d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,3 +35,71 @@ jobs: - name: Pack component (same validation the registry runs on upload) run: compote -W component pack --project-dir components/microlink --name microlink + + examples: + name: "build ${{ matrix.example }} (${{ matrix.idf_version }})" + runs-on: ubuntu-latest + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + example: + - basic_connect + - cellular_connect + - cellular_heartbeat + - failover_connect + - rebind_test + idf_version: [ v6.0.1 ] + include: + # Early warning for the next ESP-IDF minor release -- allowed to + # fail without turning the whole workflow red. + - example: basic_connect + idf_version: release-v6.0 + continue_on_error: true + steps: + - uses: actions/checkout@v7 + with: + submodules: recursive + + - uses: espressif/esp-idf-ci-action@v1 + continue-on-error: ${{ matrix.continue_on_error == true }} + with: + esp_idf_version: ${{ matrix.idf_version }} + target: esp32s3 + path: "examples/${{ matrix.example }}" + command: "idf.py build" + + config-variants: + name: "build ${{ matrix.variant }}" + runs-on: ubuntu-latest + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + # Each variant overlays ci/sdkconfig.ci. on top of an + # existing example's own sdkconfig.defaults. SDKCONFIG_DEFAULTS + # replaces the implicit default entirely, so the base file has to + # be listed again alongside the overlay. + - variant: zerocopy + base_example: basic_connect + - variant: minimal + base_example: basic_connect + - variant: cellular-zerocopy + base_example: cellular_connect + steps: + - uses: actions/checkout@v7 + with: + submodules: recursive + + - name: Copy overlay into example dir + run: cp "ci/sdkconfig.ci.${{ matrix.variant }}" "examples/${{ matrix.base_example }}/sdkconfig.ci" + + - uses: espressif/esp-idf-ci-action@v1 + with: + esp_idf_version: v6.0.1 + target: esp32s3 + path: "examples/${{ matrix.base_example }}" + # esp-idf-ci-action interpolates `command` inside a single-quoted + # bash -c '...' -- only double quotes are safe to use here. + command: "idf.py -DSDKCONFIG_DEFAULTS=\"sdkconfig.defaults;sdkconfig.ci\" build" diff --git a/.github/workflows/upstream-drift.yml b/.github/workflows/upstream-drift.yml new file mode 100644 index 0000000..c5c8e2e --- /dev/null +++ b/.github/workflows/upstream-drift.yml @@ -0,0 +1,86 @@ +name: Upstream drift + +on: + schedule: + - cron: '17 6 * * 1' # Mondays 06:17 UTC + workflow_dispatch: + +permissions: + contents: read + issues: write + +concurrency: + group: upstream-drift + cancel-in-progress: false + +jobs: + check: + runs-on: ubuntu-latest + timeout-minutes: 10 + env: + GH_TOKEN: ${{ github.token }} + UPSTREAM: CamM2325/microlink + TITLE: 'Upstream drift: CamM2325/microlink' + # PR numbers already accounted for in UPSTREAM_PRS.md (absorbed or + # deliberately skipped with a recorded reason) -- excluded so the + # tracking issue only ever shows genuinely new drift. + KNOWN_PRS: '20 21 22 23 24 25 14 3' + steps: + - uses: actions/checkout@v7 + with: { fetch-depth: 0 } + + - name: Collect drift + id: drift + run: | + set -euo pipefail + git fetch --no-tags --quiet origin main # this repo IS the fork; origin's parent is upstream + # This repo is a real GitHub fork of $UPSTREAM. We don't have a local + # remote pointed at it (no need to -- GitHub tracks the fork relationship + # directly), so fetch it by URL for the comparison. + git fetch --no-tags --quiet "https://github.com/$UPSTREAM.git" main:refs/remotes/upstream-check/main + + # `git cherry ` lists commits in missing from + # by patch-id, prefixing '-' when already present. Reversing + # the arguments gives upstream commits we lack. + COMMITS="$(git cherry -v HEAD upstream-check/main | sed -n 's/^+ //p' || true)" + + PRS="" + while read -r n title; do + [ -n "$n" ] || continue + case " $KNOWN_PRS " in *" $n "*) continue ;; esac + PRS="${PRS}- [#${n}](https://github.com/${UPSTREAM}/pull/${n}) — ${title}"$'\n' + done < <(gh pr list --repo "$UPSTREAM" --state open --limit 100 \ + --json number,title --jq '.[] | "\(.number) \(.title)"') + + { + echo 'body<> "$GITHUB_OUTPUT" + + if [ -n "$COMMITS$PRS" ]; then echo 'drifted=true' >> "$GITHUB_OUTPUT"; + else echo 'drifted=false' >> "$GITHUB_OUTPUT"; fi + + - name: Open / update / close the tracking issue + run: | + set -euo pipefail + NUM="$(gh issue list --state open --label upstream-drift --limit 1 --json number --jq '.[0].number // empty')" + if [ "${{ steps.drift.outputs.drifted }}" = 'true' ]; then + if [ -n "$NUM" ]; then + gh issue edit "$NUM" --body "${{ steps.drift.outputs.body }}" + else + gh issue create --title "$TITLE" --label upstream-drift \ + --body "${{ steps.drift.outputs.body }}" + fi + elif [ -n "$NUM" ]; then + gh issue close "$NUM" --comment 'No upstream drift remaining.' + fi diff --git a/ci/sdkconfig.ci.cellular-zerocopy b/ci/sdkconfig.ci.cellular-zerocopy new file mode 100644 index 0000000..9a83680 --- /dev/null +++ b/ci/sdkconfig.ci.cellular-zerocopy @@ -0,0 +1,6 @@ +# Config-variant overlay for CI: zero-copy WireGuard receive combined with +# cellular, on top of cellular_connect's own sdkconfig.defaults (which +# already has CONFIG_ML_ENABLE_CELLULAR=y). Catches interaction bugs between +# the two features that neither one alone would surface. +# See .github/workflows/ci.yml's config-variants job. +CONFIG_ML_ZERO_COPY_WG=y diff --git a/ci/sdkconfig.ci.minimal b/ci/sdkconfig.ci.minimal new file mode 100644 index 0000000..064d49b --- /dev/null +++ b/ci/sdkconfig.ci.minimal @@ -0,0 +1,8 @@ +# Config-variant overlay for CI: everything optional switched off, on top of +# basic_connect's own sdkconfig.defaults -- catches REQUIRES/ifdef mistakes +# that only show up when optional features are compiled OUT, not in. +# See .github/workflows/ci.yml's config-variants job. +CONFIG_ML_ENABLE_CONFIG_HTTPD=n +CONFIG_ML_ZERO_COPY_WG=n +CONFIG_ML_ENABLE_CELLULAR=n +CONFIG_ML_ENABLE_NET_SWITCH=n diff --git a/ci/sdkconfig.ci.zerocopy b/ci/sdkconfig.ci.zerocopy new file mode 100644 index 0000000..6ec08d0 --- /dev/null +++ b/ci/sdkconfig.ci.zerocopy @@ -0,0 +1,4 @@ +# Config-variant overlay for CI: exercises the zero-copy WireGuard receive +# path (contributed by dj-oyu). Applied on top of basic_connect's own +# sdkconfig.defaults -- see .github/workflows/ci.yml's config-variants job. +CONFIG_ML_ZERO_COPY_WG=y diff --git a/components/microlink/CMakeLists.txt b/components/microlink/CMakeLists.txt index b856c39..72f1b4e 100644 --- a/components/microlink/CMakeLists.txt +++ b/components/microlink/CMakeLists.txt @@ -36,17 +36,19 @@ set(REQUIRES driver esp_driver_tsens esp_http_server -) - -if(CONFIG_ML_ENABLE_CELLULAR) # ml_cellular.c / ml_at_socket.c include driver/uart.h and driver/gpio.h. On # ESP-IDF 6.x those headers live in esp_driver_uart / esp_driver_gpio, not in # the umbrella `driver` component above -- `driver` only PRIV_REQUIRES # esp_driver_gpio (private, doesn't propagate) and doesn't require - # esp_driver_uart at all. Without these, any CONFIG_ML_ENABLE_CELLULAR=y - # build fails to find both headers. - list(APPEND REQUIRES esp_driver_uart esp_driver_gpio) -endif() + # esp_driver_uart at all. Unconditional, not gated on CONFIG_ML_ENABLE_CELLULAR: + # REQUIRES/PRIV_REQUIRES are resolved during ESP-IDF's component-requirements + # expansion pass, which runs before Kconfig is available -- REQUIRES can never + # be conditioned on a CONFIG_* value (only SRCS can, in the later registration + # pass). Confirmed empirically: a Kconfig-gated REQUIRES here silently no-ops + # in every environment tried (PlatformIO and real ESP-IDF Docker CI alike). + esp_driver_uart + esp_driver_gpio +) idf_component_register( SRCS ${SRCS} diff --git a/components/microlink/src/ml_config_httpd.c b/components/microlink/src/ml_config_httpd.c index 8033dc0..392042c 100644 --- a/components/microlink/src/ml_config_httpd.c +++ b/components/microlink/src/ml_config_httpd.c @@ -163,7 +163,11 @@ static void config_load_peers(ml_config_ctx_t *ctx) { size_t rlen = stored_len; err = nvs_get_blob(ctx->nvs, NVS_KEY_PEERS, tmp, &rlen); if (err == ESP_OK) { - uint8_t old_count = tmp[0]; + /* old_count is widened to uint16_t before the comparison: comparing the + * uint8_t directly against ML_CONFIG_MAX_ALLOWED_PEERS is tautological + * whenever that Kconfig value is >= 255 (true for the default of 512), + * which -Werror=type-limits correctly flags as always-true. */ + uint16_t old_count = tmp[0]; uint16_t count = (old_count <= ML_CONFIG_MAX_ALLOWED_PEERS) ? old_count : 0; memset(&ctx->peer_list, 0, sizeof(ctx->peer_list)); ctx->peer_list.count = count; diff --git a/examples/failover_connect/main/CMakeLists.txt b/examples/failover_connect/main/CMakeLists.txt index 00ee335..98dd7ad 100644 --- a/examples/failover_connect/main/CMakeLists.txt +++ b/examples/failover_connect/main/CMakeLists.txt @@ -1,4 +1,5 @@ idf_component_register( SRCS "main.c" INCLUDE_DIRS "." + REQUIRES microlink nvs_flash esp_netif esp_event driver )