From ea8d618489aabf484f5fc3dedea48a16a66d6714 Mon Sep 17 00:00:00 2001 From: Afri <220195275+l5yth@users.noreply.github.com> Date: Tue, 6 Oct 2026 15:27:55 +0200 Subject: [PATCH] ci: upload test results with codecov-action v7 and move to checkout v7 (#975) --- .github/dependabot.yml | 3 ++ .github/workflows/codeql.yml | 2 +- .github/workflows/docker.yml | 4 +- .github/workflows/javascript.yml | 7 ++- .github/workflows/license.yml | 2 +- .github/workflows/mobile.yml | 2 +- .github/workflows/nix.yml | 2 +- .github/workflows/python.yml | 7 ++- .github/workflows/release.yml | 6 +-- .github/workflows/ruby.yml | 7 ++- .github/workflows/rust.yml | 2 +- CHANGELOG.md | 1 + tests/test_dependency_manifests.py | 77 +++++++++++++++++++++++++++++- 13 files changed, 105 insertions(+), 17 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 14c9b7ee..c3836f22 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -26,6 +26,9 @@ updates: directory: "/" schedule: interval: "weekly" + # Room for one open pull request per action in use, so no update waits + # behind the others (the default limit is 5). + open-pull-requests-limit: 20 - package-ecosystem: "cargo" directory: "/matrix" schedule: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 16f383c6..75da394a 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -44,7 +44,7 @@ jobs: build-mode: none steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v7 - name: Initialize CodeQL uses: github/codeql-action/init@v4 with: diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index bced3171..d482dbf6 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -54,7 +54,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: # Full history + tags so `git describe` can compute the baked app # version below (the published image ships without .git). @@ -165,7 +165,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 diff --git a/.github/workflows/javascript.yml b/.github/workflows/javascript.yml index 9c245bab..aaa09c45 100644 --- a/.github/workflows/javascript.yml +++ b/.github/workflows/javascript.yml @@ -34,7 +34,7 @@ jobs: run: working-directory: web steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v7 - name: Set up Node.js 22 uses: actions/setup-node@v4 with: @@ -55,8 +55,11 @@ jobs: env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - name: Upload test results to Codecov - uses: codecov/test-results-action@v1 + if: ${{ !cancelled() }} + uses: codecov/codecov-action@v7 with: token: ${{ secrets.CODECOV_TOKEN }} + report_type: test_results files: web/reports/javascript-junit.xml flags: frontend + fail_ci_if_error: false diff --git a/.github/workflows/license.yml b/.github/workflows/license.yml index 330d3f54..a2f8d5a1 100644 --- a/.github/workflows/license.yml +++ b/.github/workflows/license.yml @@ -12,6 +12,6 @@ jobs: headers: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Check Apache license headers run: ./scripts/check-license-headers.sh diff --git a/.github/workflows/mobile.yml b/.github/workflows/mobile.yml index d3410004..be1e75de 100644 --- a/.github/workflows/mobile.yml +++ b/.github/workflows/mobile.yml @@ -42,7 +42,7 @@ jobs: run: working-directory: ./app steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v7 - name: Set up Flutter uses: subosito/flutter-action@v2 with: diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index 9c9e7bf9..5cf14d86 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -25,7 +25,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Install Nix uses: cachix/install-nix-action@v31 with: diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 0ff113a2..1ab28fd5 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -50,7 +50,7 @@ jobs: matrix: python-version: ['3.13', '3.14'] steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v7 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v7 with: @@ -79,11 +79,14 @@ jobs: env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - name: Upload test results to Codecov - uses: codecov/test-results-action@v1 + if: ${{ !cancelled() }} + uses: codecov/codecov-action@v7 with: token: ${{ secrets.CODECOV_TOKEN }} + report_type: test_results files: reports/python-junit.xml flags: python-ingestor + fail_ci_if_error: false - name: Lint with black run: | black --check ./ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 043da253..07c59db8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -40,7 +40,7 @@ jobs: env: TAG_NAME: ${{ github.ref_name }} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v7 - name: Configure Android local.properties run: | cat > android/local.properties <<'EOF' @@ -80,7 +80,7 @@ jobs: env: TAG_NAME: ${{ github.ref_name }} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v7 - name: Set up Flutter uses: subosito/flutter-action@v2 with: @@ -117,7 +117,7 @@ jobs: PUBLISH_RELEASE: "false" if: ${{ startsWith(github.ref, 'refs/tags/v') }} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v7 - name: Check tag format run: | set -euo pipefail diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index 86508863..fcf8b938 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -41,7 +41,7 @@ jobs: ruby-version: ['3.4', '4.0'] steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v7 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: @@ -59,11 +59,14 @@ jobs: --format RspecJunitFormatter \ --out tmp/test-results/rspec.xml - name: Upload test results to Codecov - uses: codecov/test-results-action@v1 + if: ${{ !cancelled() }} + uses: codecov/codecov-action@v7 with: token: ${{ secrets.CODECOV_TOKEN }} + report_type: test_results files: ./web/tmp/test-results/rspec.xml flags: sinatra-${{ matrix.ruby-version }} + fail_ci_if_error: false - name: Upload coverage to Codecov uses: codecov/codecov-action@v7 with: diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index c4a47839..38c8b604 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -32,7 +32,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v7 - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable - name: Cache Cargo registry diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f64aa86..46c278bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ * Data: `MESHCORE_TELEMETRY_POLL_24H_EXEMPT` reaches every packaged deployment - `docker-compose.yml`, both `data/Dockerfile` stages and the NixOS module (`meshcoreTelemetryPoll24hExempt`); setting it in `.env` had no effect under Compose or NixOS (SPEC PX5) * Docs: the README documents `MESHTASTIC_PSK_B64` (SPEC PX7) * CI: the env-surface check runs in pytest whenever a deployment surface changes, and also checks names with digits, names read through helpers, and retired names (SPEC PX6/PX8) +* CI: test results reach Codecov also when tests fail; workflows use codecov-action v7 and checkout v7; Dependabot opens up to 20 GitHub Actions update PRs at a time (SPEC DW1/DW2/DW4) * Docker: the root `Dockerfile` builds the same image as `web/Dockerfile`, including Chromium for Open Graph previews (skip it with `--build-arg WITH_OG_IMAGE=0`), the custom pages directory, and the `APP_VERSION` build arg (SPEC DF1) * Docs: `SECURITY.md` says how to report a vulnerability privately (SPEC SEC1) * Data/Web: Reticulum reports a frequency and LoRa preset, read from the RNS config (SPEC RL1-RL3) diff --git a/tests/test_dependency_manifests.py b/tests/test_dependency_manifests.py index b2b17d20..cdfc2c73 100644 --- a/tests/test_dependency_manifests.py +++ b/tests/test_dependency_manifests.py @@ -14,7 +14,7 @@ """Guard the dependency manifests that Dependabot, CI, and the images consume. -Three regressions are pinned here: +Four regressions are pinned here: * ``.github/dependabot.yml`` named ecosystems GitHub does not document (``ruby`` and ``python`` instead of ``bundler`` and ``pip``). The file was @@ -24,6 +24,9 @@ Three regressions are pinned here: * ``data/requirements.txt`` carried only ``>=`` floors plus the dev tools, so the ingestor image installed black and pytest and whatever release was newest on build day. +* The ``github-actions`` entry kept Dependabot's default limit of 5 open pull + requests, so its 2026-10-04 and 2026-10-05 runs computed the + ``actions/checkout`` major and then dropped it behind the open updates. """ from __future__ import annotations @@ -121,6 +124,11 @@ RNS_FLOOR = Version("1.5.1") # A trailing ``# comment``. A ``#`` that belongs to a value is never preceded # by whitespace in these files, so this cannot cut a value short. _COMMENT = re.compile(r"(^|\s)#.*$") +# A workflow step's ``uses: owner/repo[/sub-path]@ref``, capturing the part +# before ``@``. +_USES = re.compile( + r"^[ \t]*(?:-[ \t]+)?uses:[ \t]*[\"']?([^@\s\"'.][^@\s\"']*)@", re.MULTILINE +) # A YAML block-sequence item, capturing its indentation and its body. _ITEM = re.compile(r"^(?P\s+)-\s+(?P.*)$") # A YAML ``key: value`` line; nested keys are matched at any depth. @@ -387,6 +395,51 @@ def test_dependabot_covers_every_manifest_directory() -> None: assert not not_weekly, f"Dependabot entries not on a weekly schedule: {not_weekly}" +def _action_repositories(text: str) -> set[str]: + """Return the ``owner/repo`` of every remote action a workflow file uses. + + Dependabot updates a repository once for all its sub-path actions + (``github/codeql-action/init`` and ``/analyze``), so a sub-path folds into + its repository. Local actions (``./``) and ``docker://`` images are not + GitHub Actions updates and are left out. + + Args: + text: The contents of a workflow file. + + Returns: + The ``owner/repo`` names, without duplicates. + """ + + return { + "/".join(name.split("/")[:2]) for name in _USES.findall(text) if ":" not in name + } + + +def test_actions_pr_limit_covers_every_action() -> None: + """The ``github-actions`` limit fits one open pull request per action repository. + + Dependabot drops an update it has computed once the entry already has + ``open-pull-requests-limit`` pull requests open (5 by default). A limit at + or above the number of action repositories the workflows use keeps every + update from waiting behind the others. + """ + + entries = _dependabot_entries(DEPENDABOT.read_text(encoding="utf-8")) + actions = [e for e in entries if e.get("package-ecosystem") == "github-actions"] + workflows = REPO_ROOT / _WORKFLOWS + files = sorted([*workflows.glob("*.yml"), *workflows.glob("*.yaml")]) + used = set().union( + *(_action_repositories(f.read_text(encoding="utf-8")) for f in files) + ) + assert len(actions) == 1, "expected one github-actions entry in dependabot.yml" + assert used, "no `uses: owner/action@ref` lines parsed from .github/workflows" + limit = int(actions[0].get("open-pull-requests-limit", "5")) + assert limit >= len(used), ( + f"github-actions open-pull-requests-limit is {limit}, " + f"below the {len(used)} distinct actions in use" + ) + + @requires_git def test_gemfile_lock_is_not_git_ignored() -> None: """No committed ``.gitignore`` rule ignores ``web/Gemfile.lock``. @@ -520,6 +573,28 @@ def test_dependabot_parser_reads_only_top_level_update_items() -> None: ] +def test_action_repositories_fold_sub_paths_and_skip_local_actions() -> None: + """Actions count once per repository; local actions and images not at all.""" + + text = """jobs: + build: + steps: + - uses: actions/checkout@v7 + - name: Init + uses: "github/codeql-action/init@v4" + - uses: 'github/codeql-action/analyze@v4' + - uses: ./.github/actions/local + - uses: docker://alpine@sha256:0123abcd + - uses: dtolnay/rust-toolchain@stable +""" + + assert _action_repositories(text) == { + "actions/checkout", + "github/codeql-action", + "dtolnay/rust-toolchain", + } + + def test_required_entries_map_manifests_to_their_directories() -> None: """Manifests map to ecosystem and directory; other files need no entry."""