|
| 1 | +name: cross-library (reusable) |
| 2 | + |
| 3 | +# Reusable engine for compile-testing a downstream wolfSSL product against the |
| 4 | +# wolfSSL in this checkout. It builds wolfSSL once from this checkout (the PR |
| 5 | +# merge commit) with the flags a product needs, then compiles the product |
| 6 | +# against that install at both the product's default-branch HEAD and its highest |
| 7 | +# release tag (compile-only, no `make check`). |
| 8 | +# |
| 9 | +# The wolfSSL build runs once and is shared: the build-wolfssl job installs |
| 10 | +# wolfSSL and uploads it as an artifact, and the compile matrix (head, latest) |
| 11 | +# downloads that artifact instead of rebuilding. This halves the wolfSSL builds |
| 12 | +# per product from two to one. |
| 13 | + |
| 14 | +on: |
| 15 | + workflow_call: |
| 16 | + inputs: |
| 17 | + product: |
| 18 | + description: 'Short product label (used for the job name)' |
| 19 | + required: true |
| 20 | + type: string |
| 21 | + repo: |
| 22 | + description: 'Product source: "owner/repo" shorthand or a full git URL' |
| 23 | + required: true |
| 24 | + type: string |
| 25 | + wolfssl_configure: |
| 26 | + description: 'Configure flags for the local wolfSSL build' |
| 27 | + required: true |
| 28 | + type: string |
| 29 | + product_configure: |
| 30 | + description: "The product's own ./configure flags" |
| 31 | + required: false |
| 32 | + default: '' |
| 33 | + type: string |
| 34 | + script: |
| 35 | + description: 'Build script name under .github/workflows/cross-library/scripts/' |
| 36 | + required: true |
| 37 | + type: string |
| 38 | + apt_packages: |
| 39 | + description: 'Extra apt packages needed to build the product' |
| 40 | + required: false |
| 41 | + default: '' |
| 42 | + type: string |
| 43 | + container: |
| 44 | + description: 'Container image to build in (e.g. ubuntu:24.04, debian:13)' |
| 45 | + required: false |
| 46 | + default: 'ubuntu:24.04' |
| 47 | + type: string |
| 48 | + |
| 49 | +jobs: |
| 50 | + # Build wolfSSL once and publish the install dir. Both compile legs (head, |
| 51 | + # latest) consume it, so this runs a single time per product instead of once |
| 52 | + # per leg. |
| 53 | + build-wolfssl: |
| 54 | + name: Build wolfSSL (${{ inputs.product }}) |
| 55 | + if: ${{ (github.repository_owner == 'wolfssl') && (github.event_name != 'pull_request' || github.event.pull_request.draft == false) }} |
| 56 | + runs-on: ubuntu-24.04 |
| 57 | + container: |
| 58 | + image: ${{ inputs.container }} |
| 59 | + timeout-minutes: 20 |
| 60 | + steps: |
| 61 | + # Minimal-image containers ship without git/toolchain; install them |
| 62 | + # before checkout. Product-specific extras come from apt_packages. |
| 63 | + - name: Install build tools |
| 64 | + run: | |
| 65 | + set -eux |
| 66 | + export DEBIAN_FRONTEND=noninteractive |
| 67 | + apt-get update |
| 68 | + apt-get install -y --no-install-recommends \ |
| 69 | + build-essential autoconf automake libtool pkg-config \ |
| 70 | + git ca-certificates ${{ inputs.apt_packages }} |
| 71 | +
|
| 72 | + # Building only needs the commit under test, not history. The break check |
| 73 | + # that needs history runs in the compile job, not here. |
| 74 | + - name: Checkout wolfSSL |
| 75 | + uses: actions/checkout@v5 |
| 76 | + with: |
| 77 | + fetch-depth: 1 |
| 78 | + fetch-tags: false |
| 79 | + |
| 80 | + # Container job: the bind-mounted workspace is owned by a different uid, |
| 81 | + # so mark it safe or git refuses to operate on it ("dubious ownership"). |
| 82 | + - name: Mark workspace safe for git |
| 83 | + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" |
| 84 | + |
| 85 | + # Build wolfSSL and install to a local dir. The configure flags go through |
| 86 | + # an env var (not inline ${{ }}) so a quoted CFLAGS/C_EXTRA_FLAGS group |
| 87 | + # survives GitHub's expansion intact; the string is trusted caller input |
| 88 | + # that build-wolfssl.sh eval's. |
| 89 | + - name: Build and install wolfSSL |
| 90 | + env: |
| 91 | + WOLFSSL_CONFIGURE: ${{ inputs.wolfssl_configure }} |
| 92 | + run: | |
| 93 | + .github/workflows/cross-library/scripts/build-wolfssl.sh \ |
| 94 | + "$GITHUB_WORKSPACE" \ |
| 95 | + "$GITHUB_WORKSPACE/wolfssl-install" \ |
| 96 | + "$WOLFSSL_CONFIGURE" |
| 97 | +
|
| 98 | + # Pack as a tar so libtool symlinks, exec bits, and the absolute-path |
| 99 | + # .la/.pc files survive the artifact round-trip. The compile job unpacks to |
| 100 | + # the same $GITHUB_WORKSPACE path, so those baked-in paths stay valid. |
| 101 | + - name: Pack the wolfSSL install |
| 102 | + run: tar -C "$GITHUB_WORKSPACE" -czf wolfssl-install.tar.gz wolfssl-install |
| 103 | + |
| 104 | + - name: Upload the wolfSSL install |
| 105 | + uses: actions/upload-artifact@v4 |
| 106 | + with: |
| 107 | + name: wolfssl-install-${{ inputs.product }} |
| 108 | + path: wolfssl-install.tar.gz |
| 109 | + retention-days: 1 |
| 110 | + |
| 111 | + # Compile the product against the shared wolfSSL, once per ref_mode: |
| 112 | + # head -> HEAD of the product's default branch (master/main, auto-detected) |
| 113 | + # latest -> the product's highest version tag (polled at run time) |
| 114 | + compile: |
| 115 | + name: Compile ${{ inputs.product }} (${{ matrix.ref_mode }}) |
| 116 | + needs: build-wolfssl |
| 117 | + runs-on: ubuntu-24.04 |
| 118 | + container: |
| 119 | + image: ${{ inputs.container }} |
| 120 | + timeout-minutes: 25 |
| 121 | + strategy: |
| 122 | + fail-fast: false |
| 123 | + matrix: |
| 124 | + ref_mode: [ head, latest ] |
| 125 | + steps: |
| 126 | + # Minimal-image containers ship without git/toolchain; install them |
| 127 | + # before checkout. Product-specific extras come from apt_packages. |
| 128 | + - name: Install build tools |
| 129 | + run: | |
| 130 | + set -eux |
| 131 | + export DEBIAN_FRONTEND=noninteractive |
| 132 | + apt-get update |
| 133 | + apt-get install -y --no-install-recommends \ |
| 134 | + build-essential autoconf automake libtool pkg-config \ |
| 135 | + git ca-certificates ${{ inputs.apt_packages }} |
| 136 | +
|
| 137 | + # This job does not build wolfSSL, but the latest leg still checks out |
| 138 | + # wolfSSL history because check-break.sh scans commit messages here. The |
| 139 | + # head leg never waives a break, so a depth-1 checkout is enough for it. |
| 140 | + - name: Checkout wolfSSL |
| 141 | + uses: actions/checkout@v5 |
| 142 | + with: |
| 143 | + fetch-depth: 1 |
| 144 | + fetch-tags: false |
| 145 | + |
| 146 | + # This is a container job: the workspace is a host bind-mount owned by a |
| 147 | + # different uid than the (root) container user, so git refuses to operate |
| 148 | + # on it ("detected dubious ownership") unless it is marked safe. actions/ |
| 149 | + # checkout marks it safe only under its own *temporary* HOME, which our |
| 150 | + # run: steps (HOME=/github/home) do not inherit, so check-break.sh's |
| 151 | + # `git tag`/`git log` would fail and, with stderr hidden, look like "no |
| 152 | + # break declared". Mark it safe under this HOME for all following steps. |
| 153 | + - name: Mark workspace safe for git |
| 154 | + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" |
| 155 | + |
| 156 | + # The `latest` leg is the only one that reads wolfSSL history: it needs the |
| 157 | + # commit messages check-break.sh scans (the last two release cycles) plus |
| 158 | + # the two newest release tags, and no file contents, so --filter=tree:0 |
| 159 | + # fetches commits+tags only and turns a 912 MiB full clone into ~1 MiB on |
| 160 | + # top of the source. The exclude boundary is the THIRD-newest release, not |
| 161 | + # the scan base, because --shallow-exclude severs the ref it names and |
| 162 | + # severing the scan base would drop it from `git tag --merged HEAD` and |
| 163 | + # silently halve the window. Tags come in by explicit refspec (never |
| 164 | + # --tags, which re-pulls every tag's history), and note a server without |
| 165 | + # uploadpack.allowFilter silently ignores the filter and clones in full. |
| 166 | + - name: Deepen wolfSSL history for the break check |
| 167 | + if: ${{ matrix.ref_mode == 'latest' }} |
| 168 | + shell: bash |
| 169 | + run: | |
| 170 | + set -euo pipefail |
| 171 | +
|
| 172 | + # Release-tag selection MUST agree with check-break.sh's |
| 173 | + # `--list 'v*-stable' --sort=-v:refname`. `sort -Vr` matches git's |
| 174 | + # -v:refname on this tag set (verified incl. double-digit minors, |
| 175 | + # e.g. v5.9.10 > v5.9.2). The assertion below catches any drift. |
| 176 | + mapfile -t T < <(git ls-remote --tags --refs origin 'v*-stable' \ |
| 177 | + | sed 's#.*refs/tags/##' | sort -Vr) |
| 178 | + echo "Newest release tags: ${T[*]:0:3}" |
| 179 | +
|
| 180 | + if [ "${#T[@]}" -lt 3 ]; then |
| 181 | + echo "Fewer than three release tags; falling back to full history." |
| 182 | + git fetch --unshallow --filter=tree:0 --tags origin |
| 183 | + else |
| 184 | + # Fetch by ref (not raw SHA) so this does not depend on the server |
| 185 | + # allowing reachable-SHA1-in-want. |
| 186 | + git fetch --no-tags --filter=tree:0 --shallow-exclude="${T[2]}" origin \ |
| 187 | + "$GITHUB_REF" \ |
| 188 | + "refs/tags/${T[0]}:refs/tags/${T[0]}" \ |
| 189 | + "refs/tags/${T[1]}:refs/tags/${T[1]}" |
| 190 | + fi |
| 191 | +
|
| 192 | + # Fail LOUD if this step and check-break.sh disagree on the scan base. |
| 193 | + # check-break.sh:61 falls back to the NEWEST tag when the second line |
| 194 | + # is missing, which would quietly halve the window rather than error. |
| 195 | + rel="$(git tag --merged HEAD --sort=-v:refname --list 'v*-stable')" |
| 196 | + base="$(printf '%s\n' "$rel" | sed -n '2p')" |
| 197 | + echo "Deepened to $(git rev-list --count HEAD) commits; scan base='${base}'" |
| 198 | + if [ -z "$base" ]; then |
| 199 | + echo "::error::Deepen produced no usable scan base; check-break.sh would scan the wrong window." |
| 200 | + printf 'Release tags merged into HEAD: %s\n' "${rel:-<none>}" |
| 201 | + exit 1 |
| 202 | + fi |
| 203 | +
|
| 204 | + - name: Download the wolfSSL install |
| 205 | + uses: actions/download-artifact@v4 |
| 206 | + with: |
| 207 | + name: wolfssl-install-${{ inputs.product }} |
| 208 | + |
| 209 | + # Unpack to the same path wolfSSL was built at, keeping the .la/.pc |
| 210 | + # absolute paths valid. |
| 211 | + - name: Unpack the wolfSSL install |
| 212 | + run: tar -C "$GITHUB_WORKSPACE" -xzf wolfssl-install.tar.gz |
| 213 | + |
| 214 | + # Resolve the concrete ref to compile: the highest tag (latest) or the |
| 215 | + # default branch (head). Used both for the build and the break check. |
| 216 | + - name: Resolve product ref |
| 217 | + id: ref |
| 218 | + env: |
| 219 | + REPO: ${{ inputs.repo }} |
| 220 | + run: | |
| 221 | + ref="$(.github/workflows/cross-library/scripts/resolve-ref.sh "$REPO" "${{ matrix.ref_mode }}")" |
| 222 | + echo "Resolved ${{ matrix.ref_mode }} ref for $REPO: $ref" |
| 223 | + echo "ref=$ref" >> "$GITHUB_OUTPUT" |
| 224 | +
|
| 225 | + # Compile-only (never `make check`). A compile failure is allowed ONLY if |
| 226 | + # a wolfSSL commit since the last release tag declared it with a |
| 227 | + # `breaks-<product>=<ref>` token (see check-break.sh); otherwise the job |
| 228 | + # fails, forcing intentional breaks to be recorded in a commit. |
| 229 | + # PRODUCT_CONFIGURE is intentionally unquoted so multiple flags split. |
| 230 | + - name: Compile ${{ inputs.product }} against wolfSSL |
| 231 | + env: |
| 232 | + REPO: ${{ inputs.repo }} |
| 233 | + PRODUCT_CONFIGURE: ${{ inputs.product_configure }} |
| 234 | + PRODUCT: ${{ inputs.product }} |
| 235 | + REF: ${{ steps.ref.outputs.ref }} |
| 236 | + MODE: ${{ matrix.ref_mode }} |
| 237 | + run: | |
| 238 | + S=.github/workflows/cross-library/scripts |
| 239 | + set +e |
| 240 | + # shellcheck disable=SC2086 # $PRODUCT_CONFIGURE must word-split into flags |
| 241 | + "$S/${{ inputs.script }}" -t "$REF" \ |
| 242 | + "$GITHUB_WORKSPACE/wolfssl-install" "$REPO" $PRODUCT_CONFIGURE |
| 243 | + rc=$? |
| 244 | + set -e |
| 245 | +
|
| 246 | + # The breaks-<product>= mechanism applies ONLY to the latest release |
| 247 | + # tag. A head/master break is never waivable, never consults the |
| 248 | + # ledger, and must be fixed. |
| 249 | + if [ "$rc" -eq 0 ]; then |
| 250 | + if [ "$MODE" = "latest" ] && "$S/check-break.sh" "$PRODUCT" "$REF" >/tmp/brk 2>/dev/null; then |
| 251 | + echo "::warning::$PRODUCT ($REF) compiled OK but a break is still declared, remove the stale breaks-$PRODUCT=$REF token:" |
| 252 | + cat /tmp/brk |
| 253 | + fi |
| 254 | + exit 0 |
| 255 | + fi |
| 256 | +
|
| 257 | + echo "===== $PRODUCT ($REF) failed to compile against this wolfSSL =====" |
| 258 | +
|
| 259 | + if [ "$MODE" = "latest" ]; then |
| 260 | + # Released, immutable tag: allowed only if the exact tag is declared. |
| 261 | + if "$S/check-break.sh" "$PRODUCT" "$REF"; then |
| 262 | + echo "::warning::$PRODUCT $REF failed to compile, but this break is DECLARED (see above). Treating as a known/tracked break." |
| 263 | + exit 0 |
| 264 | + fi |
| 265 | + echo "::error::$PRODUCT $REF (latest release) no longer compiles against this wolfSSL and no break is declared." |
| 266 | + echo "That tag is a released version and cannot be changed. If wolfSSL is intentionally dropping" |
| 267 | + echo "compatibility with it, record the break so CI tracks it and this job passes. Add this exact" |
| 268 | + echo "token to a commit message in this PR (the release must be named explicitly):" |
| 269 | + echo " breaks-$PRODUCT=$REF" |
| 270 | + echo "Otherwise, rework the change so $PRODUCT $REF still builds." |
| 271 | + else |
| 272 | + # Product master/HEAD: NEVER waivable, no breaks- declaration exists. |
| 273 | + echo "::error::$PRODUCT $REF (default branch) no longer compiles against this wolfSSL." |
| 274 | + echo "A master/HEAD break cannot be waived; there is no breaks- declaration for it." |
| 275 | + echo "$PRODUCT $REF must stay compatible with wolfSSL master. Fix it by either:" |
| 276 | + echo " * reworking this PR so $PRODUCT $REF builds against wolfSSL master again, or" |
| 277 | + echo " * putting up a matching fix on $PRODUCT's $REF branch, then re-running this job." |
| 278 | + fi |
| 279 | + exit 1 |
0 commit comments