Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/actions/install-apt-deps/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ runs:

- name: Install packages
shell: bash
env:
APT_CACHE_HIT: ${{ steps.apt-cache.outputs.cache-hit }}
run: |
export DEBIAN_FRONTEND=noninteractive
RETRIES=${{ inputs.retries }}
Expand All @@ -62,6 +64,18 @@ runs:
if [ "${{ inputs.no-install-recommends }}" = "true" ]; then
NO_REC="--no-install-recommends"
fi

# Fast path: on cache hit the .debs are already pre-seeded into
# /var/cache/apt/archives. Try installing directly first; if that
# fails (e.g. the cached .debs were superseded in the index) fall
# through to the regular update + install path.
if [ "$APT_CACHE_HIT" = "true" ]; then
if sudo apt-get install -y $NO_REC ${{ inputs.packages }}; then
exit 0
fi
echo "::warning::install from cached .debs failed, falling back to apt-get update"
fi

for i in $(seq 1 $RETRIES); do
if sudo apt-get update -q && \
sudo apt-get install -y $NO_REC ${{ inputs.packages }}; then
Expand Down
95 changes: 95 additions & 0 deletions .github/actions/wait-for-smoke/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
name: 'Wait for Smoke Test'
description: 'Polls the Smoke Test workflow for the current commit and fails if it failed.'

# Designed to be the leading job in pull_request-triggered workflows so that
# expensive integration CI does not run unless the smoke build passes.
#
# Push events bypass the wait entirely (we still get smoke results for those
# pushes, but other CI is not gated on push). For drafts, callers should
# skip dependent jobs via `if: github.event.pull_request.draft == false` -
# this action will still pass through if smoke is skipped or absent.

inputs:
workflow:
description: 'Name of the smoke workflow file to wait on'
required: false
default: 'smoke-test.yml'
timeout-seconds:
description: 'Maximum time to wait for smoke to complete'
required: false
default: '1800'
poll-seconds:
description: 'Polling interval'
required: false
default: '20'
github-token:
description: 'GITHUB_TOKEN with actions:read permission'
required: true

runs:
using: 'composite'
steps:
- name: Wait for smoke
shell: bash
env:
GH_TOKEN: ${{ inputs.github-token }}
SMOKE_WORKFLOW: ${{ inputs.workflow }}
TIMEOUT: ${{ inputs.timeout-seconds }}
POLL: ${{ inputs.poll-seconds }}
REPO: ${{ github.repository }}
run: |
set -u
# Only gate pull_request events. Push events are not gated.
if [ "${{ github.event_name }}" != "pull_request" ]; then
echo "Not a pull_request event - skipping smoke gate."
exit 0
fi

HEAD_SHA="${{ github.event.pull_request.head.sha }}"
echo "Waiting for $SMOKE_WORKFLOW on $HEAD_SHA (timeout ${TIMEOUT}s)"

START=$(date +%s)
while :; do
NOW=$(date +%s)
ELAPSED=$((NOW - START))
if [ "$ELAPSED" -ge "$TIMEOUT" ]; then
echo "::error::Timed out after ${TIMEOUT}s waiting for $SMOKE_WORKFLOW on $HEAD_SHA"
exit 1
fi

# Look up the latest run for this workflow + head SHA.
RUN_JSON=$(gh api \
"repos/${REPO}/actions/workflows/${SMOKE_WORKFLOW}/runs?head_sha=${HEAD_SHA}&per_page=1" \
2>/dev/null || echo '{}')

STATUS=$(echo "$RUN_JSON" | jq -r '.workflow_runs[0].status // "missing"')
CONCLUSION=$(echo "$RUN_JSON" | jq -r '.workflow_runs[0].conclusion // ""')
RUN_URL=$(echo "$RUN_JSON" | jq -r '.workflow_runs[0].html_url // ""')

case "$STATUS" in
completed)
case "$CONCLUSION" in
success)
echo "Smoke test passed: $RUN_URL"
exit 0
;;
skipped|neutral)
echo "Smoke test was $CONCLUSION - treating as pass: $RUN_URL"
exit 0
;;
*)
echo "::error::Smoke test concluded as '$CONCLUSION': $RUN_URL"
exit 1
;;
esac
;;
missing)
echo "[$ELAPSED s] No smoke run yet for $HEAD_SHA"
;;
*)
echo "[$ELAPSED s] Smoke status=$STATUS ($RUN_URL)"
;;
esac

sleep "$POLL"
done
99 changes: 99 additions & 0 deletions .github/scripts/check-headers.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#!/usr/bin/env bash
#
# check-headers.sh
#
# Verifies that every public-facing wolfSSL header compiles standalone
# from a fresh consumer's perspective:
#
# #include <wolfssl/options.h>
# #include <wolfssl/...the header...>
# int main(void) { return 0; }
#
# Catches the common breakage where a header silently relies on a
# transitive include from an earlier `.c` file and stops compiling
# when downstream code includes it first.
#
# Requires:
# * ./configure has been run (so wolfssl/options.h exists).
# * gcc and standard build env.
#
# Usage:
# .github/scripts/check-headers.sh # scan default header set
# .github/scripts/check-headers.sh <files> # scan a specific list

set -u

ROOT="$(git rev-parse --show-toplevel)"
cd "$ROOT" || exit 2

if [ ! -f wolfssl/options.h ]; then
echo "::error::wolfssl/options.h not found - run ./configure first" >&2
exit 2
fi

CC="${CC:-gcc}"
GHA="${GITHUB_ACTIONS:-}"

emit() {
local file="$1" msg="$2"
if [ -n "$GHA" ]; then
printf '::error file=%s,line=1,title=header-self-include::%s\n' "$file" "$msg"
else
printf '%s: %s\n' "$file" "$msg"
fi
}

# Default scope: public wolfssl headers excluding vendor/port subdirs and
# files that are intentionally not standalone-includable.
if [ "$#" -gt 0 ]; then
HEADERS=("$@")
else
# Exclusions:
# * generated / private / test-data headers.
# * wolfcrypt math backends (tfm vs sp_int are mutually exclusive).
# * port/* headers whose first-line vendor SDK include can't be
# satisfied in a generic CI environment (mcapi.h, kcapi.h,
# em_device.h, fsl_dcp.h, hw/inout.h, etc.) or that reference
# vendor-only types. Fix the offending header's vendor #include
# with an #ifdef guard and drop the exclusion in a follow-up.
mapfile -t HEADERS < <(
git ls-files 'wolfssl/*.h' 'wolfssl/wolfcrypt/*.h' \
'wolfssl/wolfcrypt/port/**/*.h' 'wolfssl/openssl/*.h' \
| grep -vE '^wolfssl/(options|internal|certs_test|certs_test_sm|debug-trace-error-codes|debug-untrace-error-codes)\.h$' \
| grep -vE '^wolfssl/wolfcrypt/(fips_test|selftest|tfm)\.h$' \
| grep -vE '^wolfssl/wolfcrypt/port/aria/aria-crypt(ocb)?\.h$' \
| grep -vE '^wolfssl/wolfcrypt/port/autosar/(CryIf|Crypto)\.h$' \
| grep -vE '^wolfssl/wolfcrypt/port/caam/(caam_driver|caam_qnx|wolfcaam_hash)\.h$' \
| grep -vE '^wolfssl/wolfcrypt/port/kcapi/' \
| grep -vE '^wolfssl/wolfcrypt/port/nxp/(dcp_port|se050_port)\.h$' \
| grep -vE '^wolfssl/wolfcrypt/port/Renesas/(renesas_fspsm_internal|renesas-rx64-hw-crypt|renesas-tsip-crypt|renesas_tsip_internal)\.h$' \
| grep -vE '^wolfssl/wolfcrypt/port/silabs/silabs_aes\.h$'
)
fi

TMPDIR="$(mktemp -d)"
trap 'rm -rf "$TMPDIR"' EXIT

FAIL=0
PASS=0
for h in "${HEADERS[@]}"; do
[ -f "$h" ] || continue
cat > "$TMPDIR/test.c" <<EOF
#include <wolfssl/options.h>
#include <$h>
int main(void) { return 0; }
EOF
if out="$("$CC" -I. -c -o /dev/null "$TMPDIR/test.c" 2>&1)"; then
PASS=$((PASS + 1))
else
FAIL=$((FAIL + 1))
first_err="$(printf '%s' "$out" | grep -E 'error:' | head -1 | sed 's/.*error: //')"
emit "$h" "header does not compile standalone: ${first_err:-(see build log)}"
if [ -z "$GHA" ]; then
printf '%s\n' "$out" | head -8 | sed 's/^/ /'
fi
fi
done

echo "check-headers: $PASS pass, $FAIL fail"
[ "$FAIL" -eq 0 ]
Loading
Loading