Repository navigation
Fix: Fenrir F-10621 #18053
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Ubuntu-Macos-Windows Tests | |
| # START OF COMMON SECTION | |
| on: | |
| push: | |
| branches: [ 'master', 'release/**' ] | |
| # Docs-only changes cannot affect the build/test matrix - skip the | |
| # run for them. Keep this list narrow (markdown + doc/ only); | |
| # do not add cert/test data extensions here. | |
| paths-ignore: | |
| - '**/*.md' | |
| - 'doc/**' | |
| pull_request: | |
| types: [opened, synchronize, reopened, ready_for_review] | |
| branches: [ '*' ] | |
| paths-ignore: | |
| - '**/*.md' | |
| - 'doc/**' | |
| # Run the build and update the ccache on: | |
| # - Scheduled: weekday mornings at 10:00 UTC to keep the ccache. | |
| # This reseeds both platforms from scratch (a manual run reseeds | |
| # linux from scratch, but only refreshes macOS - see below). | |
| # - Any push to master that changed headers or the build system since the | |
| # last seed (linux shards only; macOS is refreshed by the cron and | |
| # manual runs, since its runner pool is small and its cold-cache cost | |
| # minor). | |
| # Note that Windows builds are skipped on a seed run since it uses Visual | |
| # Studio and has no ccache to seed. | |
| # | |
| # The two platforms do not seed identically, so do not read "the seed" as | |
| # one mechanism. Linux wires ccache by hand: CCACHE_RECACHE and no restore | |
| # on a cron or manual seed run (a push seed is incremental), and a save | |
| # only on seed runs. macOS goes through the shared ccache-setup composite, | |
| # which sets CCACHE_RECACHE and skips the restore only on the cron (not on | |
| # a dispatch seed) and saves on every run that is not read-only, | |
| # i.e. every non-PR run, release pushes included. So a dispatch refills an | |
| # empty macOS cache but only layers onto a stale one; only the cron | |
| # rebuilds macOS from scratch. Left as-is deliberately; the note is here | |
| # so the difference is not rediscovered from a surprising cache. | |
| schedule: | |
| - cron: '0 10 * * 1-5' | |
| # Runs the same seed on demand. Needed whenever the cache is empty and | |
| # waiting up to a day for the next cron is not acceptable - after a | |
| # stretch where this workflow was not running, for instance, since a | |
| # cache untouched for 7 days is evicted and only the seed refills it. | |
| # Every PR run stays cold until it is refilled. | |
| workflow_dispatch: | |
| inputs: | |
| seed: | |
| description: > | |
| Seed the ccache: compile only, no tests, and save the result. | |
| Untick to run the full checks instead. | |
| type: boolean | |
| default: true | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| # Merge to master triggers a reseed. Do not let a subseuent merge cancel it. | |
| cancel-in-progress: ${{ github.ref != 'refs/heads/master' }} | |
| permissions: | |
| contents: read | |
| # END OF COMMON SECTION | |
| jobs: | |
| # Determine if the merged changed anything which could significantly impact | |
| # the ccache. | |
| seed_gate: | |
| name: seed needed | |
| if: ${{ (github.repository_owner == 'wolfssl') && (github.ref == 'refs/heads/master') && (github.event_name == 'push' || github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.seed != 'false')) }} | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 5 | |
| permissions: | |
| contents: read | |
| actions: read | |
| outputs: | |
| seed: ${{ steps.check.outputs.seed }} | |
| steps: | |
| - id: check | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| REPO: ${{ github.repository }} | |
| SHA: ${{ github.sha }} | |
| BEFORE: ${{ github.event.before }} | |
| run: | | |
| # Compare with the commit of the oldest shard seed rather than the | |
| # previous push, so a reseed cancelled by a later push (the master | |
| # runs share one concurrency group) is redone by that push. | |
| # The compare endpoint lists at most 300 changed files, so a push | |
| # that large is treated as wide-reaching without looking further. | |
| seed=true | |
| last=$(gh api -X GET "repos/$REPO/actions/caches" \ | |
| -f key=os-check-linux-ccache- -f ref=refs/heads/master -F per_page=100 \ | |
| --jq '[.actions_caches[] | select(.key | test("^os-check-linux-ccache-[1-4]-master-[0-9a-f]{40}$"))] | |
| | group_by(.key | split("-")[4]) | |
| | if length == 4 then map(max_by(.created_at)) | min_by(.created_at).key | |
| else "incomplete" end') || last= | |
| base=${last##*-} | |
| [[ $base =~ ^[0-9a-f]{40}$ ]] || base=$BEFORE | |
| # Build inputs the ccache is sensitive to. Headers live outside | |
| # wolfssl/ too: examples/configs holds the user_settings variants | |
| # the matrix stages, and wolfcrypt/src has a few private ones. | |
| wide='^((wolfssl|tests|wolfcrypt|examples|testsuite)/.*\.h' | |
| wide+='|wolfcrypt/src/misc\.c|configure\.ac|m4/.*|.*\.am' | |
| wide+='|\.github/configs/os-check-.*\.json' | |
| wide+='|\.github/workflows/os-check\.yml)$' | |
| if [ "$GITHUB_EVENT_NAME" != push ]; then | |
| # A seed of an already seeded commit would fail to save. | |
| [ "$base" != "$SHA" ] || seed=false | |
| echo "last seed ${base:0:10}, commit ${SHA:0:10}, seeding: $seed" | |
| elif [ "$last" = incomplete ]; then | |
| # Fewer than four shard seeds exist, so there is no complete set | |
| # to compare against; comparing with the previous push instead | |
| # would keep declining the seed that would refill it. | |
| echo "incomplete shard seed set, seeding" | |
| elif files=$(gh api "repos/$REPO/compare/$base...$SHA" --jq '.files[]?.filename' | sort -u); then | |
| count=$(printf '%s\n' "$files" | grep -c . || true) | |
| if [ "$count" -lt 300 ] && ! printf '%s\n' "$files" | grep -qE "$wide"; then | |
| seed=false | |
| fi | |
| echo "last seed ${base:0:10}, changed files: $count, wide reach: $seed" | |
| else | |
| echo "compare unavailable, seeding to be safe" | |
| fi | |
| echo "seed=$seed" >> "$GITHUB_OUTPUT" | |
| # Ubuntu config matrix. macOS is covered separately by make_check_macos | |
| # below with a curated subset; configs here either have equivalent macOS | |
| # coverage there or exercise no Darwin-specific code. | |
| # | |
| # The config list is built by a small fixed pool of shard runners: every | |
| # shard job runs the generic .github/scripts/parallel-make-check.py on | |
| # the same JSON list below with --shard K/N, which deals the configs | |
| # across the N shards greedily by their "minutes" weight so every shard | |
| # carries a similar load. Within a shard each config builds in its own | |
| # out-of-tree ("VPATH") build directory off one checkout/autogen, and | |
| # the checks run on a pool of one-per-CPU worker threads, longest first; | |
| # per-config times and thread/CPU efficiency land in each shard's step | |
| # summary (same machinery as smoke-test.yml). bubblewrap lets the script | |
| # tests re-exec under bwrap --unshare-net so concurrent checks cannot | |
| # collide on TCP/UDP ports (do not set AM_BWRAPPED here - that would | |
| # disable it). | |
| make_check_linux: | |
| name: make check linux | |
| needs: [seed_gate] | |
| permissions: | |
| contents: read | |
| actions: write # allows deletion of old caches | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| shard: [1, 2, 3, 4] | |
| # !cancelled() keeps this job alive when seed_gate is skipped. | |
| if: ${{ !cancelled() && (github.repository_owner == 'wolfssl') && (github.event_name != 'pull_request' || github.event.pull_request.draft == false) }} | |
| runs-on: ubuntu-24.04 | |
| # Measured cold on a completely empty cache (2026-08-03): ~102 | |
| # thread-minutes per shard / 4 worker threads, i.e. 24-27 min of wall | |
| # per shard including checkout/deps/autogen; warm is well under 10. | |
| # The timeout is set well above the cold figure on purpose - a cold | |
| # run is normal whenever the seed has not run recently, and a shard | |
| # dying on the timeout would look like a test failure rather than a | |
| # cold cache. | |
| timeout-minutes: 60 | |
| env: | |
| CCACHE_MAXSIZE: 500M | |
| # Skip every step of a declined seed so the job shows success. | |
| SEED_DECLINED: ${{ needs.seed_gate.outputs.seed == 'false' }} | |
| steps: | |
| - uses: actions/checkout@v5 | |
| if: ${{ env.SEED_DECLINED != 'true' }} | |
| # tlslite-ng is consumed by scripts/multi-msg-record.test (run from | |
| # `make check`); without it that test is SKIPped. | |
| - uses: actions/setup-python@v6 | |
| if: ${{ env.SEED_DECLINED != 'true' }} | |
| with: | |
| python-version: '3.x' | |
| - run: pip install 'tlslite-ng==0.8.2' | |
| if: ${{ env.SEED_DECLINED != 'true' }} | |
| - name: Install dependencies | |
| if: ${{ env.SEED_DECLINED != 'true' }} | |
| uses: ./.github/actions/install-apt-deps | |
| with: | |
| packages: autoconf automake libtool build-essential bubblewrap ccache gcc-multilib zlib1g-dev | |
| ghcr-debs-tag: ubuntu-24.04-minimal | |
| # Ubuntu 24.04 can restrict unprivileged user namespaces via AppArmor, | |
| # which would stop the test scripts from re-execing under | |
| # bwrap --unshare-net (their port-isolation mechanism). | |
| - name: Allow unprivileged user namespaces (for bwrap) | |
| if: ${{ env.SEED_DECLINED != 'true' }} | |
| run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 || true | |
| # ccache's default cache dir (XDG ~/.cache/ccache) is what the | |
| # actions/cache step below saves; pin it explicitly so the two | |
| # cannot drift apart (e.g. if a later change sets CCACHE_DIR). | |
| - name: Pin ccache directory | |
| if: ${{ env.SEED_DECLINED != 'true' }} | |
| run: echo "CCACHE_DIR=$HOME/.cache/ccache" >> "$GITHUB_ENV" | |
| # PRs restore the cache a seed run writes but never save it, so PR | |
| # runs add no per-shard ccache entries to the Actions cache. Only a | |
| # seed run (a push to master, the cron, or a manual dispatch) saves, | |
| # below. | |
| # Don't restore on cron or manual since we will rebuild from scratch | |
| # using CCACHE_RECACHE. Restore would bring the stale cache entry to the | |
| # top of the Github LRU, above newly written entries. | |
| - name: Restore ccache | |
| if: ${{ env.SEED_DECLINED != 'true' && !(github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.seed != 'false')) }} | |
| uses: actions/cache/restore@v5 | |
| with: | |
| path: ~/.cache/ccache | |
| # Per-shard cache lineage: each shard compiles a distinct config | |
| # subset. Re-balancing "minutes" can move configs between shards; | |
| # that costs one rebuild of whatever moved. | |
| key: os-check-linux-ccache-${{ matrix.shard }}-${{ github.base_ref || github.ref_name }}-${{ github.sha }} | |
| restore-keys: | | |
| os-check-linux-ccache-${{ matrix.shard }}-${{ github.base_ref || github.ref_name }}- | |
| os-check-linux-ccache-${{ matrix.shard }}- | |
| os-check-linux-ccache- | |
| - name: Force fresh compiles on a reseed | |
| if: ${{ env.SEED_DECLINED != 'true' && (github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.seed != 'false')) }} | |
| run: echo "CCACHE_RECACHE=1" >> "$GITHUB_ENV" | |
| - name: autogen | |
| if: ${{ env.SEED_DECLINED != 'true' }} | |
| run: | | |
| ccache -z | |
| ./autogen.sh | |
| # The config list lives in .github/configs/os-check-linux.json; add new | |
| # configs there as new entries (a "comment" key is allowed for notes). | |
| # It is kept out of this file on purpose: a single `run:` step is | |
| # capped at 21000 characters by GitHub, and inlining the list as a | |
| # heredoc silently pushed this workflow past that cap - GitHub then | |
| # refused to load the whole file and every run failed with zero jobs. | |
| # "minutes" is the expected duration driving longest-first scheduling | |
| # and shard balancing: take it from the Minutes column of a previous | |
| # run's step summary, or omit it for a new config (defaults to 1) and | |
| # refresh later - a stale value only packs the schedule worse. The | |
| # list is kept sorted by minutes for readability, but the schedule | |
| # sorts by the values, not list order. | |
| # The CFLAGS that were previously passed to configure are applied at | |
| # make time via --cflags, unchanged. --private-dir=certs gives every | |
| # build dir its own certs/ copy: crl-gen-openssl.test writes generated | |
| # CRLs under certs/crl/, which would race through the shared VPATH | |
| # certs symlink. | |
| - name: Build and make check this shard's configs (parallel, out-of-tree) | |
| if: ${{ env.SEED_DECLINED != 'true' }} | |
| run: | | |
| .github/scripts/parallel-make-check.py \ | |
| ${{ (github.event_name == 'schedule' || (github.event_name == 'push' && github.ref == 'refs/heads/master') || (github.event_name == 'workflow_dispatch' && github.event.inputs.seed != 'false')) && '--build-only' || '' }} \ | |
| --shard "${{ matrix.shard }}/${{ strategy.job-total }}" \ | |
| --cflags='-pedantic -Wdeclaration-after-statement -Wnull-dereference -Wno-overlength-strings -DTEST_LIBWOLFSSL_SOURCES_INCLUSION_SEQUENCE' \ | |
| --private-dir=certs .github/configs/os-check-linux.json | |
| # Seed runs (a push to master, the cron, or a manual dispatch with | |
| # `seed` ticked) refresh the master-scoped ccache that PR runs restore | |
| # above; PR and release-branch runs never save, so PRs add nothing. | |
| - name: Save ccache (seed only) | |
| if: ${{ env.SEED_DECLINED != 'true' && (github.event_name == 'schedule' || (github.event_name == 'push' && github.ref == 'refs/heads/master') || (github.event_name == 'workflow_dispatch' && github.event.inputs.seed != 'false')) }} | |
| uses: actions/cache/save@v5 | |
| with: | |
| path: ~/.cache/ccache | |
| key: os-check-linux-ccache-${{ matrix.shard }}-${{ github.ref_name }}-${{ github.sha }} | |
| # Delete old ccache seed entries. Note that cache-janitor.yml cleans | |
| # entries which do not go through this flow. | |
| - name: Prune this shard's stale seeds | |
| continue-on-error: true | |
| if: ${{ env.SEED_DECLINED != 'true' && (github.event_name == 'schedule' || (github.event_name == 'push' && github.ref == 'refs/heads/master') || (github.event_name == 'workflow_dispatch' && github.event.inputs.seed != 'false')) }} | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| REPO: ${{ github.repository }} | |
| REF: ${{ github.ref }} | |
| PREFIX: os-check-linux-ccache-${{ matrix.shard }}-${{ github.ref_name }}- | |
| run: | | |
| # Housekeeping must not fail the build: an entry that vanished | |
| # between the list and the delete is fine, anything else is | |
| # surfaced as an annotation. | |
| gh api -X GET "repos/$REPO/actions/caches" -f key="$PREFIX" -f ref="$REF" \ | |
| -f sort=created_at -f direction=desc -F per_page=100 \ | |
| --jq '.actions_caches[1:][] | "\(.id) \(.key)"' | | |
| while read -r id key; do | |
| if err=$(gh api -X DELETE "repos/$REPO/actions/caches/$id" 2>&1 >/dev/null); then | |
| echo "deleted $key" | |
| elif [[ $err == *"HTTP 404"* ]]; then | |
| echo "$key already gone" | |
| else | |
| echo "::error::could not delete $key: $err" | |
| fi | |
| done | |
| - name: ccache stats | |
| if: ${{ always() && env.SEED_DECLINED != 'true' }} | |
| run: ccache -s || true | |
| - name: Upload logs on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| retention-days: 7 | |
| name: os-check-linux-logs-${{ matrix.shard }} | |
| path: | | |
| build-*/make-check.log | |
| build-*/test-suite.log | |
| build-*/config.log | |
| if-no-files-found: ignore | |
| # Curated macOS subset. Each config exists for a Darwin-specific reason; | |
| # do not add entries that only re-test platform-agnostic crypto already | |
| # covered by the corresponding Linux run. | |
| # | |
| # All configs build on ONE macos runner via | |
| # .github/scripts/parallel-make-check.py, each in its own out-of-tree | |
| # ("VPATH") build directory. Unlike Linux, the checks run one config at | |
| # a time (--threads 1): macOS has no bubblewrap, so concurrent make | |
| # checks would race on TCP/UDP ports. The user_settings_all.h entry is | |
| # the former macOS half of the make_user_settings job, and the | |
| # apple-native-cert-validation entry is the former standalone | |
| # macos-apple-native-cert-validation.yml workflow. | |
| make_check_macos: | |
| name: make check macos | |
| # Runs on PRs/pushes and on a seed run (the cron or a manual dispatch), | |
| # where it --build-only-seeds the macOS ccache like the linux shards. | |
| # Windows is the only job skipped on a seed run (no ccache to seed). | |
| # Not seeded on master pushes: macOS runner concurrency is small, so | |
| # per-merge seeds would queue PR macOS jobs behind them, and a fresh | |
| # macOS seed saves only about two minutes per PR job. The cron refreshes | |
| # it from scratch; a manual dispatch only layers onto what is there. | |
| if: ${{ (github.repository_owner == 'wolfssl') && (github.event_name != 'pull_request' || github.event.pull_request.draft == false) && (github.event_name != 'push' || github.ref != 'refs/heads/master') }} | |
| runs-on: macos-latest | |
| # Serial checks: roughly the sum of the per-config minutes plus | |
| # one-time setup, with headroom for a cold ccache. | |
| timeout-minutes: 45 | |
| steps: | |
| - uses: actions/checkout@v5 | |
| # tlslite-ng is consumed by scripts/multi-msg-record.test (run from | |
| # `make check`); without it that test is SKIPped. | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.x' | |
| - run: pip install 'tlslite-ng==0.8.2' | |
| # The macos runner images ship without autotools (the old per-config | |
| # matrix got them via wolfSSL/actions-build-autotools-project). | |
| - name: Install autotools (brew) | |
| run: brew install autoconf automake libtool | |
| # ccache via the cross-platform composite (brew install; compiler | |
| # symlinks on PATH). The script is told --cc= so it does not also | |
| # prefix the compiler with "ccache" - the PATH masquerade already | |
| # intercepts cc/clang. | |
| - name: Set up ccache | |
| uses: ./.github/actions/ccache-setup | |
| with: | |
| workflow-id: os-check-macos | |
| max-size: 500M | |
| # PRs read the seeded macOS ccache and never write it. Note this | |
| # is not the linux rule: there, saving is gated to seed runs, so | |
| # a push writes nothing. Here every non-PR run writes, seed or | |
| # not, so a release/** push and a dispatch with `seed` unticked | |
| # both land in the shared macOS cache. | |
| read-only: ${{ github.event_name == 'pull_request' }} | |
| # Same JSON config format as make_check_linux above, in | |
| # .github/configs/os-check-macos.json; "minutes" only orders the | |
| # serial schedule here (longest first). | |
| - name: Build and make check all configs (serial checks, out-of-tree) | |
| run: | | |
| .github/scripts/parallel-make-check.py \ | |
| ${{ (github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.seed != 'false')) && '--build-only' || '' }} \ | |
| --threads 1 --cc= \ | |
| --cflags='-pedantic -Wdeclaration-after-statement -Wnull-dereference -Wno-overlength-strings -DTEST_LIBWOLFSSL_SOURCES_INCLUSION_SEQUENCE' \ | |
| --private-dir=certs .github/configs/os-check-macos.json | |
| - name: ccache stats | |
| if: always() | |
| run: ccache -s || true | |
| - name: Upload logs on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| retention-days: 7 | |
| name: os-check-macos-logs | |
| path: | | |
| build-*/make-check.log | |
| build-*/test-suite.log | |
| build-*/config.log | |
| if-no-files-found: ignore | |
| windows_build: | |
| name: Windows Build Test | |
| # Skipped on any seed run - a push to master, the cron, or a manual | |
| # dispatch that left `seed` ticked: there is no ccache to seed here. | |
| if: ${{ (github.repository_owner == 'wolfssl') && (github.event_name != 'schedule') && (github.event_name != 'push' || github.ref != 'refs/heads/master') && (github.event_name != 'workflow_dispatch' || github.event.inputs.seed == 'false') && (github.event_name != 'pull_request' || github.event.pull_request.draft == false) }} | |
| runs-on: windows-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| arch: [ x64, Win32, ARM64 ] | |
| asm: [ false ] | |
| include: | |
| # Intel assembly build (x64 only): assembles the crypto .asm files | |
| # and enables the matching USE_INTEL_SPEEDUP code paths. | |
| - arch: x64 | |
| asm: true | |
| # This should be a safe limit for the tests to run. | |
| timeout-minutes: 6 | |
| env: | |
| # Path to the solution file relative to the root of the project. | |
| SOLUTION_FILE_PATH: wolfssl64.sln | |
| # Configuration type to build. | |
| # You can convert this to a build matrix if you need coverage of multiple configuration types. | |
| # https://docs.github.com/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix | |
| BUILD_CONFIGURATION: Release | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Add MSBuild to PATH | |
| uses: microsoft/setup-msbuild@v3 | |
| - name: Restore NuGet packages | |
| working-directory: ${{env.GITHUB_WORKSPACE}} | |
| run: nuget restore ${{env.SOLUTION_FILE_PATH}} | |
| - name: Build | |
| working-directory: ${{env.GITHUB_WORKSPACE}} | |
| # Add additional options to the MSBuild command line here (like platform or verbosity level). | |
| # See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference | |
| run: msbuild /m /p:PlatformToolset=v142 /p:Platform=${{matrix.arch}} /p:Configuration=${{env.BUILD_CONFIGURATION}} /p:WolfSSLIntelAsm=${{matrix.asm}} ${{env.SOLUTION_FILE_PATH}} | |
| - if: ${{ matrix.arch != 'ARM64' }} | |
| name: Run Test | |
| working-directory: ${{env.GITHUB_WORKSPACE}} | |
| run: Release/${{matrix.arch}}/testsuite.exe |