# Copyright (C) Viktor Szakats
#
# SPDX-License-Identifier: curl

name: Windows

'on':
  push:
    branches:
      - master
      - '*/ci'
    paths-ignore:
      - '**/*.md'
      - '.circleci/**'
      - 'appveyor.*'
      - 'packages/**'
      - 'plan9/**'
      - 'projects/**'
      - 'winbuild/**'
  pull_request:
    branches:
      - master
    paths-ignore:
      - '**/*.md'
      - '.circleci/**'
      - 'appveyor.*'
      - 'packages/**'
      - 'plan9/**'
      - 'projects/**'
      - 'winbuild/**'

concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
  cancel-in-progress: true

permissions: {}

env:
  CURL_CI: github

jobs:
  cygwin:
    name: "cygwin, ${{ matrix.build == 'cmake' && 'CM' || 'AM' }} ${{ matrix.platform }} ${{ matrix.name }}"
    runs-on: windows-latest
    timeout-minutes: 15
    defaults:
      run:
        shell: D:\cygwin\bin\bash.exe '{0}'
    env:
      MAKEFLAGS: -j 5
      SHELLOPTS: 'igncr'
    strategy:
      matrix:
        include:
          - { build: 'automake', platform: 'x86_64', tflags: 'skiprun', config: '--with-openssl', install: 'libssl-devel libssh2-devel', name: 'openssl R' }
          - { build: 'cmake'   , platform: 'x86_64', tflags: ''       , config: '-DENABLE_DEBUG=ON -DCURL_USE_OPENSSL=ON -DENABLE_THREADED_RESOLVER=OFF', install: 'libssl-devel libssh2-devel', name: 'openssl' }
      fail-fast: false
    steps:
      - run: git config --global core.autocrlf input
        shell: pwsh
      - uses: cygwin/cygwin-install-action@f2009323764960f80959895c7bc3bb30210afe4d # v5
        with:
          platform: ${{ matrix.platform }}
          site: https://mirrors.kernel.org/sourceware/cygwin/
          work-vol: 'D:'
          # https://cygwin.com/cgi-bin2/package-grep.cgi
          packages: >-
            autoconf libtool gcc-core gcc-g++ binutils
            ${{ matrix.build }} make ninja
            openssh
            libpsl-devel
            zlib-devel
            libbrotli-devel
            libzstd-devel
            libnghttp2-devel
            ${{ matrix.install }}

      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
        with:
          persist-credentials: false

      - name: 'autoreconf'
        if: ${{ matrix.build == 'automake' }}
        timeout-minutes: 2
        run: |
          PATH=/usr/bin
          autoreconf -fi

      - name: 'configure'
        timeout-minutes: 5
        run: |
          PATH=/usr/bin
          if [ '${{ matrix.build }}' = 'cmake' ]; then
            cmake -B bld -G Ninja -D_CURL_PREFILL=ON ${options} \
              -DCMAKE_UNITY_BUILD=ON -DCMAKE_UNITY_BUILD_BATCH_SIZE=30 -DCURL_TEST_BUNDLES=ON \
              -DCURL_WERROR=ON \
              ${{ matrix.config }}
          else
            mkdir bld && cd bld && ../configure --enable-unity --enable-test-bundles --enable-warnings --enable-werror \
              --prefix="${HOME}"/install \
              --with-libssh2 \
              --disable-dependency-tracking \
              ${{ matrix.config }}
          fi

      - name: 'configure log'
        if: ${{ !cancelled() }}
        run: |
          PATH=/usr/bin
          cat bld/config.log bld/CMakeFiles/CMake*.yaml 2>/dev/null || true

      - name: 'curl_config.h'
        run: |
          PATH=/usr/bin
          echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::'
          grep -F '#define' bld/lib/curl_config.h | sort || true

      - name: 'build'
        timeout-minutes: 10
        run: |
          PATH=/usr/bin
          if [ '${{ matrix.build }}' = 'cmake' ]; then
            cmake --build bld
          else
            make -C bld V=1 install
          fi

      - name: 'curl version'
        timeout-minutes: 1
        run: |
          PATH=/usr/bin
          find . \( -name '*.exe' -o -name '*.dll' -o -name '*.a' \) -exec file '{}' \;
          if [ '${{ matrix.build }}' = 'cmake' ]; then
            PATH="$PWD/bld/lib:$PATH"
          fi
          bld/src/curl.exe --disable --version

      - name: 'build tests'
        if: ${{ matrix.tflags != 'skipall' }}
        timeout-minutes: 15
        run: |
          PATH=/usr/bin
          if [ '${{ matrix.build }}' = 'cmake' ]; then
            cmake --build bld --target testdeps
          else
            make -C bld V=1 -C tests
          fi

      - name: 'run tests'
        if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }}
        timeout-minutes: 15
        run: |
          PATH=/usr/bin
          export TFLAGS='-j8 ${{ matrix.tflags }}'
          if [ -x "$(cygpath "${SYSTEMROOT}/System32/curl.exe")" ]; then
            TFLAGS+=" -ac $(cygpath "${SYSTEMROOT}/System32/curl.exe")"
          fi
          if [ '${{ matrix.build }}' = 'cmake' ]; then
            PATH="$PWD/bld/lib:$PATH"
            cmake --build bld --target test-ci
          else
            make -C bld V=1 test-ci
          fi

      - name: 'build examples'
        if: ${{ matrix.build == 'cmake' }}
        timeout-minutes: 5
        run: |
          PATH=/usr/bin
          if [ '${{ matrix.build }}' = 'cmake' ]; then
            cmake --build bld --target curl-examples
          else
            make -C bld V=1 examples
          fi

  msys2:  # both msys and mingw-w64
    name: "${{ matrix.sys == 'msys' && 'msys2' || 'mingw' }}, ${{ matrix.build == 'cmake' && 'CM' || 'AM' }} ${{ matrix.env }} ${{ matrix.name }} ${{ matrix.test }}"
    runs-on: ${{ matrix.image || 'windows-latest' }}
    timeout-minutes: 15
    defaults:
      run:
        shell: msys2 {0}
    env:
      MAKEFLAGS: -j 5
    strategy:
      matrix:
        include:
          # MSYS
          - { build: 'autotools', sys: 'msys'      , env: 'x86_64'       , tflags: ''       , config: '--enable-debug --with-openssl --disable-threaded-resolver --disable-proxy', install: 'openssl-devel libssh2-devel', name: '!proxy' }
          - { build: 'autotools', sys: 'msys'      , env: 'x86_64'       , tflags: 'skiprun', config: '--enable-debug --with-openssl --disable-threaded-resolver', install: 'openssl-devel libssh2-devel', name: 'default' }
          - { build: 'cmake'    , sys: 'msys'      , env: 'x86_64'       , tflags: ''       , config: '-DENABLE_DEBUG=ON -DENABLE_THREADED_RESOLVER=OFF', install: 'openssl-devel libssh2-devel', name: 'default' }
          - { build: 'autotools', sys: 'msys'      , env: 'x86_64'       , tflags: ''       , config: '--with-openssl', install: 'openssl-devel libssh2-devel', name: 'default R' }
          # MinGW
          - { build: 'autotools', sys: 'mingw64'   , env: 'x86_64'       , tflags: 'skiprun', config: '--enable-debug --with-openssl --disable-threaded-resolver --disable-curldebug --enable-static=no --without-zlib', install: 'mingw-w64-x86_64-openssl mingw-w64-x86_64-libssh2', name: 'default' }
          - { build: 'autotools', sys: 'mingw64'   , env: 'x86_64'       , tflags: ''       , config: '--enable-debug --with-openssl --enable-windows-unicode --enable-ares --with-openssl-quic', install: 'mingw-w64-x86_64-openssl mingw-w64-x86_64-nghttp3 mingw-w64-x86_64-libssh2', name: 'c-ares U' }
          - { build: 'cmake'    , sys: 'mingw64'   , env: 'x86_64'       , tflags: ''       , config: '-DENABLE_DEBUG=ON  -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DENABLE_ARES=ON', install: 'mingw-w64-x86_64-libssh2', type: 'Debug', name: 'schannel c-ares U' }
          # WARNING: libssh uses hard-coded world-writable paths (/etc/..., ~/.ssh/) to
          #          read its configuration from, making it vulnerable to attacks on
          #          Windows. Do not use this component till there is a fix for these.
          # https://github.com/curl/curl-for-win/blob/3951808deb04df9489ee17430f236ed54436f81a/libssh.sh#L6-L8
          - { build: 'cmake'    , sys: 'clang64'   , env: 'clang-x86_64' , tflags: ''       , config: '-DENABLE_DEBUG=ON  -DBUILD_SHARED_LIBS=OFF -DCURL_USE_GNUTLS=ON   -DENABLE_UNICODE=OFF -DUSE_NGTCP2=ON -DCURL_USE_LIBSSH2=OFF -DCURL_USE_LIBSSH=ON', install: 'mingw-w64-clang-x86_64-gnutls mingw-w64-clang-x86_64-nghttp3 mingw-w64-clang-x86_64-ngtcp2 mingw-w64-clang-x86_64-libssh', type: 'Debug', name: 'gnutls libssh' }
          - { build: 'cmake'    , sys: 'clangarm64', env: 'clang-aarch64', tflags: 'skiprun', config: '-DENABLE_DEBUG=OFF -DBUILD_SHARED_LIBS=ON  -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DENABLE_CURLDEBUG=ON', install: 'mingw-w64-clang-aarch64-libssh2', type: 'Release', name: 'schannel R TrackMemory', image: 'windows-11-arm' }
          - { build: 'cmake'    , sys: 'clang64'   , env: 'clang-x86_64' , tflags: 'skiprun', config: '-DENABLE_DEBUG=ON  -DBUILD_SHARED_LIBS=OFF -DCURL_USE_OPENSSL=ON  -DENABLE_UNICODE=OFF', install: 'mingw-w64-clang-x86_64-openssl mingw-w64-clang-x86_64-libssh2', type: 'Release', name: 'openssl', chkprefill: '_chkprefill' }
          - { build: 'cmake'    , sys: 'ucrt64'    , env: 'ucrt-x86_64'  , tflags: 'skiprun', config: '-DENABLE_DEBUG=OFF -DBUILD_SHARED_LIBS=ON  -DCURL_USE_SCHANNEL=ON', install: 'mingw-w64-ucrt-x86_64-libssh2', type: 'Release', test: 'uwp', name: 'schannel' }
          # { build: 'autotools', sys: 'ucrt64'    , env: 'ucrt-x86_64'  , tflags: 'skiprun', config: '--without-debug --with-schannel --enable-shared', install: 'mingw-w64-ucrt-x86_64-libssh2', type: 'Release', test: 'uwp', name: 'schannel' }
          - { build: 'cmake'    , sys: 'mingw64'   , env: 'x86_64'       , tflags: 'skiprun', config: '-DENABLE_DEBUG=ON  -DBUILD_SHARED_LIBS=ON  -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DCMAKE_VERBOSE_MAKEFILE=ON', install: 'mingw-w64-x86_64-libssh2', type: 'Debug', cflags: '-DCURL_SCHANNEL_DEV_DEBUG', name: 'schannel dev debug', image: 'windows-2025' }
          - { build: 'cmake'    , sys: 'mingw32'   , env: 'i686'         , tflags: 'skiprun', config: '-DENABLE_DEBUG=OFF -DBUILD_SHARED_LIBS=ON  -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON', install: 'mingw-w64-i686-libssh2', type: 'Release', name: 'schannel R' }
      fail-fast: false
    steps:
      - run: git config --global core.autocrlf input
        shell: pwsh

      - uses: msys2/setup-msys2@61f9e5e925871ba6c9e3e8da24ede83ea27fa91f # v2
        if: ${{ matrix.sys == 'msys' }}
        with:
          msystem: ${{ matrix.sys }}
          # https://packages.msys2.org/search
          install: >-
            gcc
            ${{ matrix.build }} ${{ matrix.build == 'autotools' && 'make' || 'ninja' }}
            diffutils
            zlib-devel
            brotli-devel
            libzstd-devel
            libnghttp2-devel
            libpsl-devel
            ${{ matrix.install }}

      - uses: msys2/setup-msys2@61f9e5e925871ba6c9e3e8da24ede83ea27fa91f # v2
        if: ${{ matrix.sys != 'msys' }}
        with:
          msystem: ${{ matrix.sys }}
          update: ${{ matrix.sys == 'clangarm64' }}  # delete this line on next msys2/setup-msys2 bump
          install: >-
            mingw-w64-${{ matrix.env }}-cc
            mingw-w64-${{ matrix.env }}-${{ matrix.build }} ${{ matrix.build == 'autotools' && 'make' || '' }}
            mingw-w64-${{ matrix.env }}-diffutils
            mingw-w64-${{ matrix.env }}-libpsl
            mingw-w64-${{ matrix.env }}-c-ares
            ${{ matrix.install }}

      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
        with:
          persist-credentials: false

      - name: 'autoreconf'
        if: ${{ matrix.build == 'autotools' }}
        timeout-minutes: 2
        run: autoreconf -fi

      - name: 'configure'
        timeout-minutes: 5
        run: |
          if [ '${{ matrix.test }}' = 'uwp' ]; then
            CPPFLAGS='-DWINSTORECOMPAT -DWINAPI_FAMILY=WINAPI_FAMILY_APP'
            if [[ '${{ matrix.env }}' != 'clang'* ]]; then
              specs="$(realpath gcc-specs-uwp)"
              gcc -dumpspecs | sed -e 's/-lmingwex/-lwindowsapp -lmingwex -lwindowsapp/' -e 's/-lmsvcrt/-lucrtapp/' > "${specs}"
              CFLAGS="-specs=${specs}"
              CFLAGS_CMAKE="-specs=$(cygpath -w "${specs}")"
            fi
          fi
          if [ '${{ matrix.build }}' = 'cmake' ]; then
            for _chkprefill in '' ${{ matrix.chkprefill }}; do
              if [[ '${{ matrix.env }}' = 'clang'* ]]; then
                options='-DCMAKE_C_COMPILER=clang'
              else
                options='-DCMAKE_C_COMPILER=gcc'
              fi
              [ '${{ matrix.sys }}' = 'msys' ] && options+=' -D_CURL_PREFILL=ON'
              [ '${{ matrix.test }}' = 'uwp' ] && options+=' -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0'
              [ "${_chkprefill}" = '_chkprefill' ] && options+=' -D_CURL_PREFILL=OFF'
              cmake -B "bld${_chkprefill}" -G Ninja ${options} \
                -DCMAKE_C_FLAGS="${{ matrix.cflags }} ${CFLAGS_CMAKE} ${CPPFLAGS}" \
                -DCMAKE_BUILD_TYPE='${{ matrix.type }}' \
                -DCMAKE_UNITY_BUILD=ON -DCMAKE_UNITY_BUILD_BATCH_SIZE=30 -DCURL_TEST_BUNDLES=ON \
                -DCURL_WERROR=ON \
                ${{ matrix.config }}
            done
            if [ -d bld_chkprefill ] && ! diff -u bld/lib/curl_config.h bld_chkprefill/lib/curl_config.h; then
              echo '::group::reference configure log'; cat bld_chkprefill/CMakeFiles/CMake*.yaml 2>/dev/null || true; echo '::endgroup::'
              false
            fi
          else
            export CFLAGS CPPFLAGS
            mkdir bld && cd bld && ../configure --enable-unity --enable-test-bundles --enable-warnings --enable-werror \
              --prefix="${HOME}"/install \
              --with-libssh2 \
              --disable-dependency-tracking \
              ${{ matrix.config }}
          fi

      - name: 'configure log'
        if: ${{ !cancelled() }}
        run: cat bld/config.log bld/CMakeFiles/CMake*.yaml 2>/dev/null || true

      - name: 'curl_config.h'
        run: |
          echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::'
          grep -F '#define' bld/lib/curl_config.h | sort || true

      - name: 'build'
        timeout-minutes: 10
        run: |
          if [ '${{ matrix.build }}' = 'cmake' ]; then
            cmake --build bld
          else
            make -C bld V=1 install
          fi

      - name: 'curl version'
        timeout-minutes: 1
        run: |
          if [ '${{ matrix.build }}' = 'cmake' ]; then
            PATH="$PWD/bld/lib:$PATH"
          else
            PATH="$PWD/bld/lib/.libs:$PATH"
            # avoid libtool's curl.exe wrapper
            mv bld/src/.libs/curl.exe bld/src/curl.exe
          fi
          find . \( -name '*.exe' -o -name '*.dll' -o -name '*.a' \) -exec file '{}' \;
          if [ '${{ matrix.test }}' != 'uwp' ]; then  # curl: error initializing curl library
            bld/src/curl.exe --disable --version
          fi

      - name: 'build tests'
        if: ${{ matrix.tflags != 'skipall' }}  # Save time by skipping this for autotools
        timeout-minutes: 10
        run: |
          if [ '${{ matrix.build }}' = 'cmake' ]; then
            cmake --build bld --target testdeps
          else
            make -C bld V=1 -C tests
          fi
          if [ '${{ matrix.build }}' != 'cmake' ]; then
            # avoid libtool's .exe wrappers
            mv bld/tests/http/clients/.libs/*.exe bld/tests/http/clients
            mv bld/tests/libtest/.libs/*.exe bld/tests/libtest
            mv bld/tests/server/.libs/*.exe bld/tests/server
            mv bld/tests/unit/.libs/*.exe bld/tests/unit || true
          fi

      - name: 'install test prereqs'
        if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }}
        timeout-minutes: 5
        run: |
          /usr/bin/pacman --noconfirm --noprogressbar --sync --needed openssh
          /c/ProgramData/chocolatey/choco.exe install --yes --no-progress --limit-output --timeout 180 --force stunnel || true

      - name: 'downgrade msys2-runtime'
        if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' && matrix.sys != 'msys' }}
        timeout-minutes: 2
        # Downgrade to a known good MSYS2 runtime version to avoid the performance regression
        # causing runtests.pl to run at 2-3x reduced speed.
        run: |
          if [[ "$(uname -a)" = *' 3.5'* ]]; then
            exec /usr/bin/pacman --noconfirm --noprogressbar --upgrade https://mirror.msys2.org/msys/x86_64/msys2-runtime-3.5.4-8-x86_64.pkg.tar.zst
          fi

      - name: 'run tests'
        if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }}
        timeout-minutes: 10
        run: |
          export TFLAGS='-j8 ${{ matrix.tflags }}'
          if [ '${{ matrix.sys }}' != 'msys' ]; then
            TFLAGS+=' !498'  # 'Reject too large HTTP response headers on endless redirects' HTTP, HTTP GET (runtests detecting result code 2009 instead of 56 returned by curl)
            if [[ '${{ matrix.install }}' = *'libssh2-wincng'* ]]; then
              TFLAGS+=' ~SCP ~SFTP'  # Flaky: `-8, Unable to exchange encryption keys`. https://github.com/libssh2/libssh2/issues/804
            fi
          fi
          if [ -x "$(cygpath "${SYSTEMROOT}/System32/curl.exe")" ]; then
            TFLAGS+=" -ac $(cygpath "${SYSTEMROOT}/System32/curl.exe")"
          fi
          PATH="$PATH:/c/Program Files (x86)/stunnel/bin"
          if [ '${{ matrix.build }}' = 'cmake' ]; then
            PATH="$PWD/bld/lib:$PATH"
            cmake --build bld --target test-ci
          else
            PATH="$PWD/bld/lib/.libs:$PATH"
            make -C bld V=1 test-ci
          fi

      - name: 'build examples'
        if: ${{ matrix.build == 'cmake' || (matrix.tflags == 'skipall' || matrix.tflags == 'skiprun') }}  # Save time by skipping this for autotools running tests
        timeout-minutes: 5
        run: |
          if [ '${{ matrix.build }}' = 'cmake' ]; then
            cmake --build bld --target curl-examples
          else
            make -C bld V=1 examples
          fi

  mingw-w64-standalone-downloads:
    name: 'dl-mingw, CM ${{ matrix.ver }}-${{ matrix.env }} ${{ matrix.name }}'
    runs-on: windows-latest
    timeout-minutes: 15
    defaults:
      run:
        shell: msys2 {0}
    env:
      MAKEFLAGS: -j 5
    strategy:
      matrix:
        include:
          - name: 'schannel'
            dir: 'mingw64'
            env: 'x86_64'
            ver: '15.0.1'
            url: 'https://github.com/brechtsanders/winlibs_mingw/releases/download/15.0.1-snapshot20250406posix-12.0.0-ucrt-r1/winlibs-x86_64-posix-seh-gcc-15.0.1-snapshot20250406-mingw-w64ucrt-12.0.0-r1.7z'
            config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=OFF'
            type: 'Release'
            tflags: 'skiprun'
          - name: 'schannel'
            dir: 'mingw64'
            env: 'x86_64'
            ver: '9.5.0'
            url: 'https://github.com/brechtsanders/winlibs_mingw/releases/download/9.5.0-10.0.0-msvcrt-r1/winlibs-x86_64-posix-seh-gcc-9.5.0-mingw-w64msvcrt-10.0.0-r1.7z'
            config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=OFF'
            type: 'Release'
          - name: 'schannel mbedtls U'
            dir: 'mingw64'
            env: 'x86_64'
            ver: '7.3.0'
            url: 'https://downloads.sourceforge.net/mingw-w64/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/7.3.0/threads-win32/seh/x86_64-7.3.0-release-win32-seh-rt_v5-rev0.7z'
            config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DCURL_USE_MBEDTLS=ON'
            install: mingw-w64-x86_64-mbedtls
            type: 'Release'
            tflags: 'skiprun'
          - name: 'schannel !unity'
            dir: 'mingw32'
            env: 'i686'
            ver: '6.4.0'
            url: 'https://downloads.sourceforge.net/mingw-w64/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/6.4.0/threads-win32/dwarf/i686-6.4.0-release-win32-dwarf-rt_v5-rev0.7z'
            config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=OFF -DCMAKE_UNITY_BUILD=OFF'
            type: 'Debug'
            tflags: 'skiprun'
      fail-fast: false
    steps:
      - uses: msys2/setup-msys2@d44ca8e88d8b43d56cf5670f91747359d5537f97 # v2
        with:
          msystem: ${{ matrix.dir }}
          release: false
          update: false
          cache: false
          path-type: inherit
          install: >-
            mingw-w64-${{ matrix.env }}-libpsl
            ${{ matrix.install }}

      - name: 'cache compiler (gcc ${{ matrix.ver }}-${{ matrix.env }})'
        uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4
        id: cache-compiler
        with:
          path: D:\my-cache
          key: ${{ runner.os }}-mingw-w64-${{ matrix.ver }}-${{ matrix.env }}

      - name: 'install compiler (gcc ${{ matrix.ver }}-${{ matrix.env }})'
        if: ${{ steps.cache-compiler.outputs.cache-hit != 'true' }}
        timeout-minutes: 5
        run: |
          cd /d || exit 1
          mkdir my-cache
          cd my-cache || exit 1
          curl --fail --silent --show-error --retry 3 --retry-connrefused --output pack.bin --location --proto-redir =https '${{ matrix.url }}'
          pwd
          7z x -y pack.bin >/dev/null
          rm -r -f pack.bin
          ls -l

      - run: git config --global core.autocrlf input

      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
        with:
          persist-credentials: false

      - name: 'configure'
        timeout-minutes: 5
        run: |
          PATH="/d/my-cache/${{ matrix.dir }}/bin:$PATH"
          for _chkprefill in '' ${{ matrix.chkprefill }}; do
            options=''
            [ "${_chkprefill}" = '_chkprefill' ] && options+=' -D_CURL_PREFILL=OFF'
            cmake -B "bld${_chkprefill}" -G Ninja ${options} \
              -DCMAKE_C_COMPILER=gcc \
              -DCMAKE_BUILD_TYPE='${{ matrix.type }}' \
              -DCMAKE_UNITY_BUILD=ON -DCMAKE_UNITY_BUILD_BATCH_SIZE=30 -DCURL_TEST_BUNDLES=ON \
              -DCURL_WERROR=ON \
              -DUSE_LIBIDN2=OFF \
              ${{ matrix.config }}
          done
          if [ -d bld_chkprefill ] && ! diff -u bld/lib/curl_config.h bld_chkprefill/lib/curl_config.h; then
            echo '::group::reference configure log'; cat bld_chkprefill/CMakeFiles/CMake*.yaml 2>/dev/null || true; echo '::endgroup::'
            false
          fi

      - name: 'configure log'
        if: ${{ !cancelled() }}
        run: cat bld/CMakeFiles/CMake*.yaml 2>/dev/null || true

      - name: 'curl_config.h'
        run: |
          echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::'
          grep -F '#define' bld/lib/curl_config.h | sort || true

      - name: 'build'
        timeout-minutes: 5
        run: |
          PATH="/d/my-cache/${{ matrix.dir }}/bin:$PATH"
          cmake --build bld

      - name: 'curl version'
        timeout-minutes: 1
        run: |
          PATH=/usr/bin find . \( -name '*.exe' -o -name '*.dll' -o -name '*.a' \) -exec file '{}' \;
          PATH="$PWD/bld/lib:$PATH"
          bld/src/curl.exe --disable --version

      - name: 'build tests'
        if: ${{ matrix.tflags != 'skipall' }}
        timeout-minutes: 10
        run: |
          PATH="/d/my-cache/${{ matrix.dir }}/bin:$PATH"
          cmake --build bld --target testdeps

      - name: 'install test prereqs'
        if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }}
        timeout-minutes: 5
        run: |
          /c/ProgramData/chocolatey/choco.exe install --yes --no-progress --limit-output --timeout 180 --force stunnel || true
          python3 -m pip --disable-pip-version-check --no-input --no-cache-dir install --progress-bar off --prefer-binary impacket

      - name: 'downgrade msys2-runtime'
        if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }}
        timeout-minutes: 2
        # Downgrade to a known good MSYS2 runtime version to avoid the performance regression
        # causing runtests.pl to run at 2-3x reduced speed.
        run: |
          if [[ "$(uname -a)" = *' 3.5'* ]]; then
            exec /usr/bin/pacman --noconfirm --noprogressbar --upgrade https://mirror.msys2.org/msys/x86_64/msys2-runtime-3.5.4-8-x86_64.pkg.tar.zst
          fi

      - name: 'run tests'
        if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }}
        timeout-minutes: 10
        run: |
          PATH="/d/my-cache/${{ matrix.dir }}/bin:$PATH"
          export TFLAGS='-j8 ${{ matrix.tflags }}'
          TFLAGS+=' !498'  # 'Reject too large HTTP response headers on endless redirects' HTTP, HTTP GET (runtests detecting result code 2009 instead of 56 returned by curl)
          if [ -x "$(cygpath "${SYSTEMROOT}/System32/curl.exe")" ]; then
            TFLAGS+=" -ac $(cygpath "${SYSTEMROOT}/System32/curl.exe")"
          fi
          PATH="$PWD/bld/lib:$PATH:/c/Program Files (x86)/stunnel/bin"
          cmake --build bld --target test-ci

      - name: 'build examples'
        timeout-minutes: 5
        run: |
          PATH="/d/my-cache/${{ matrix.dir }}/bin:$PATH"
          cmake --build bld --target curl-examples

  linux-cross-mingw-w64:
    name: "linux-mingw, ${{ matrix.build == 'cmake' && 'CM' || 'AM' }} ${{ matrix.compiler }}"
    runs-on: ubuntu-latest
    timeout-minutes: 15
    env:
      MAKEFLAGS: -j 5
      TRIPLET: 'x86_64-w64-mingw32'
    strategy:
      fail-fast: false
      matrix:
        include:
          - { build: 'autotools', compiler: 'gcc' }
          - { build: 'cmake'    , compiler: 'gcc' }
          - { build: 'cmake'    , compiler: 'clang-tidy' }
    steps:
      - name: 'install packages'
        timeout-minutes: 5
        # zizmor: ignore[template-injection]
        run: |
          sudo rm -f /var/lib/man-db/auto-update
          sudo apt-get -o Dpkg::Use-Pty=0 install mingw-w64 \
            ${{ matrix.compiler == 'clang-tidy' && 'clang' || '' }}

      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
        with:
          persist-credentials: false

      - name: 'autoreconf'
        if: ${{ matrix.build == 'autotools' }}
        run: autoreconf -fi

      - name: 'configure'
        run: |
          if [ '${{ matrix.build }}' = 'cmake' ]; then
            if [ '${{ matrix.compiler }}' = 'clang-tidy' ]; then
              options+=' -DCURL_CLANG_TIDY=ON'
              options+=' -DENABLE_UNICODE=ON -DUSE_SSLS_EXPORT=ON'
              options+=' -DCMAKE_C_COMPILER=clang'
              options+=" -DCMAKE_RC_COMPILER=llvm-windres-$(clang -dumpversion | cut -d '.' -f 1)"
            else
              options+=" -DCMAKE_C_COMPILER=${TRIPLET}-gcc"
            fi
            cmake -B bld -G Ninja \
              -DCMAKE_SYSTEM_NAME=Windows \
              -DCMAKE_C_COMPILER_TARGET="${TRIPLET}" \
              -DCMAKE_UNITY_BUILD=ON -DCURL_TEST_BUNDLES=ON \
              -DCURL_WERROR=ON \
              -DCURL_USE_SCHANNEL=ON -DUSE_WIN32_IDN=ON \
              -DCURL_USE_LIBPSL=OFF \
              ${options}
          else
            mkdir bld && cd bld && ../configure --enable-unity --enable-test-bundles --enable-warnings --enable-werror \
              --host="${TRIPLET}" \
              --with-schannel --with-winidn \
              --without-libpsl \
              --disable-dependency-tracking
          fi

      - name: 'configure log'
        if: ${{ !cancelled() }}
        run: cat bld/config.log bld/CMakeFiles/CMake*.yaml 2>/dev/null || true

      - name: 'curl_config.h'
        run: |
          echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::'
          grep -F '#define' bld/lib/curl_config.h | sort || true

      - name: 'build'
        run: |
          if [ '${{ matrix.build }}' = 'cmake' ]; then
            cmake --build bld
          else
            make -C bld
          fi

      - name: 'curl info'
        run: find . \( -name '*.exe' -o -name '*.dll' -o -name '*.a' \) -exec file '{}' \;

      - name: 'build tests'
        if: ${{ matrix.build == 'cmake' && matrix.compiler != 'clang-tidy' }}  # Save time by skipping this for autotools and clang-tidy
        run: |
          if [ '${{ matrix.build }}' = 'cmake' ]; then
            cmake --build bld --target testdeps
          else
            make -C bld -C tests
          fi

      - name: 'build examples'
        if: ${{ matrix.compiler != 'clang-tidy' }}  # Save time by skipping this for clang-tidy
        run: |
          if [ '${{ matrix.build }}' = 'cmake' ]; then
            cmake --build bld --target curl-examples
          else
            make -C bld examples
          fi

  wince:
    name: "mingw32ce, ${{ matrix.build == 'cmake' && 'CM' || 'AM' }} 4.4.0-arm schannel"
    runs-on: 'macos-latest'
    timeout-minutes: 10
    env:
      MAKEFLAGS: -j 4
      toolchain-version: '0.59.1'
    strategy:
      matrix:
        build: [autotools, cmake]
      fail-fast: false
    steps:
      - name: 'install packages'
        if: ${{ matrix.build == 'autotools' }}
        timeout-minutes: 5
        run: |
          echo automake libtool | xargs -Ix -n1 echo brew '"x"' > /tmp/Brewfile
          while [[ $? == 0 ]]; do for i in 1 2 3; do brew update && brew bundle install --file /tmp/Brewfile && break 2 || { echo Error: wait to try again; sleep 10; } done; false Too many retries; done

      - name: 'cache compiler (mingw32ce)'
        uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4
        id: cache-compiler
        with:
          path: ~/opt/mingw32ce
          key: ${{ runner.os }}-mingw32ce-${{ env.toolchain-version }}-amd64

      - name: 'install compiler (mingw32ce)'
        if: ${{ steps.cache-compiler.outputs.cache-hit != 'true' }}
        timeout-minutes: 5
        run: |
          cd "${HOME}" || exit 1
          curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 3 --retry-connrefused --proto-redir =https \
            --location 'https://downloads.sourceforge.net/cegcc/cegcc/${{ env.toolchain-version }}/cegcc_mingw32ce_snowleopard_r1397.tar.bz2' | tar -x
          ls -l

      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
        with:
          persist-credentials: false

      - name: 'configure'
        run: |
          PATH="$HOME/opt/mingw32ce/bin:$PATH"
          if [ '${{ matrix.build }}' = 'cmake' ]; then
            cmake -B bld -G Ninja \
              -DCMAKE_SYSTEM_NAME=WindowsCE \
              -DCMAKE_SYSTEM_VERSION=8.0 \
              -DCMAKE_SYSTEM_PROCESSOR=arm \
              -DCMAKE_C_FLAGS='-O3 -DNDEBUG' \
              -DCMAKE_C_COMPILER_TARGET=arm-mingw32ce \
              -DCMAKE_C_COMPILER=arm-mingw32ce-gcc \
              -DCMAKE_RC_COMPILER=arm-mingw32ce-windres \
              -DMINGW32CE_LIBRARY_DIR="$HOME/opt/mingw32ce/arm-mingw32ce/lib" \
              -DCMAKE_IGNORE_PREFIX_PATH="$(brew --prefix)" \
              -DCMAKE_UNITY_BUILD=ON -DCMAKE_UNITY_BUILD_BATCH_SIZE=50 -DCURL_TEST_BUNDLES=ON \
              -DBUILD_SHARED_LIBS=ON -DBUILD_STATIC_LIBS=ON -DBUILD_STATIC_CURL=OFF \
              -DCURL_WERROR=ON \
              -DCURL_USE_SCHANNEL=ON \
              -DCURL_USE_LIBPSL=OFF
          else
            autoreconf -fi
            mkdir bld && cd bld && ../configure --disable-dependency-tracking --enable-unity --enable-test-bundles --enable-warnings --enable-werror \
              --host=arm-mingw32ce \
              --with-schannel \
              --without-libpsl \
              --disable-shared
          fi

      - name: 'configure log'
        if: ${{ !cancelled() }}
        run: cat bld/config.log bld/CMakeFiles/CMake*.yaml 2>/dev/null || true

      - name: 'curl_config.h'
        run: |
          echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::'
          grep -F '#define' bld/lib/curl_config.h | sort || true

      - name: 'build'
        run: |
          PATH="$HOME/opt/mingw32ce/bin:$PATH"
          if [ '${{ matrix.build }}' = 'cmake' ]; then
            cmake --build bld
          else
            make -C bld
          fi

      - name: 'curl info'
        run: |
          find . \( -name '*.exe' -o -name '*.dll' -o -name '*.a' \) -exec file '{}' \;

      - name: 'build tests'
        if: ${{ matrix.build == 'cmake' }}  # skip for autotools to save time
        run: |
          PATH="$HOME/opt/mingw32ce/bin:$PATH"
          if [ '${{ matrix.build }}' = 'cmake' ]; then
            cmake --build bld --target testdeps
          else
            make -C bld -C tests
          fi

      - name: 'build examples'
        if: ${{ matrix.build == 'cmake' }}  # skip for autotools to save time
        run: |
          PATH="$HOME/opt/mingw32ce/bin:$PATH"
          if [ '${{ matrix.build }}' = 'cmake' ]; then
            cmake --build bld --target curl-examples
          else
            make -C bld examples
          fi

  msvc:
    name: 'msvc, CM ${{ matrix.arch }}-${{ matrix.plat }} ${{ matrix.name }}'
    runs-on: ${{ matrix.image || 'windows-latest' }}
    timeout-minutes: 15
    defaults:
      run:
        shell: msys2 {0}
    env:
      openssh_windows-version: 'v9.8.1.0p1-Preview'
      VCPKG_DISABLE_METRICS: '1'
    strategy:
      matrix:
        include:
          - name: '!ssl +examples'
            install: 'zlib libssh2[core,zlib]'
            arch: 'x64'
            plat: 'uwp'
            type: 'Debug'
            image: 'windows-2025'
            tflags: 'skiprun'
            config: >-
              -DENABLE_DEBUG=ON
              -DCURL_ENABLE_SSL=OFF
              -DUSE_WIN32_IDN=ON
              -DCURL_USE_LIBPSL=OFF

          - name: 'schannel +examples'
            install: 'zlib libssh2[core,zlib]'
            arch: 'x64'
            plat: 'windows'
            type: 'Debug'
            chkprefill: '_chkprefill'
            config: >-
              -DENABLE_DEBUG=ON
              -DCURL_USE_SCHANNEL=ON
              -DUSE_WIN32_IDN=ON -DUSE_SSLS_EXPORT=ON
              -DCURL_USE_LIBPSL=OFF

          - name: 'schannel U'
            install: 'zlib libssh2[core,zlib]'
            arch: 'arm64'
            plat: 'windows'
            type: 'Debug'
            image: 'windows-11-arm'
            openssh: 'OpenSSH-Windows'
            config: >-
              -DENABLE_DEBUG=ON
              -DCURL_USE_SCHANNEL=ON
              -DUSE_WIN32_IDN=ON -DENABLE_UNICODE=ON -DUSE_SSLS_EXPORT=ON
              -DCURL_USE_LIBPSL=OFF

      fail-fast: false
    steps:
      - uses: msys2/setup-msys2@d44ca8e88d8b43d56cf5670f91747359d5537f97 # v2
        with:
          msystem: ucrt64
          release: ${{ contains(matrix.image, 'arm') }}
          cache: ${{ contains(matrix.image, 'arm') }}
          path-type: inherit

      - name: 'vcpkg versions'
        timeout-minutes: 1
        run: |
          git -C "$VCPKG_INSTALLATION_ROOT" show --no-patch --format='%H %ai'
          vcpkg version

      - name: 'vcpkg build'
        timeout-minutes: 45
        run: vcpkg x-set-installed ${{ matrix.install }} '--triplet=${{ matrix.arch }}-${{ matrix.plat }}'

      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
        with:
          persist-credentials: false

      - name: 'configure'
        timeout-minutes: 5
        run: |
          for _chkprefill in '' ${{ matrix.chkprefill }}; do
            options=''
            if [ '${{ matrix.plat }}' = 'uwp' ]; then
              options+=' -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0'
              cflags='-DWINAPI_FAMILY=WINAPI_FAMILY_PC_APP'
              ldflags='-OPT:NOREF -OPT:NOICF -APPCONTAINER:NO'
              vsglobals=';AppxPackage=false;WindowsAppContainer=false'
            fi
            [ '${{ matrix.arch }}' = 'arm64' ] && options+=' -A ARM64'
            [ '${{ matrix.arch }}' = 'x64' ] && options+=' -A x64'
            [ '${{ matrix.arch }}' = 'x86' ] && options+=' -A Win32'
            [ "${_chkprefill}" = '_chkprefill' ] && options+=' -D_CURL_PREFILL=OFF'
            cmake -B "bld${_chkprefill}" ${options} \
              -DCMAKE_TOOLCHAIN_FILE="$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" \
              -DVCPKG_INSTALLED_DIR="$VCPKG_INSTALLATION_ROOT/installed" \
              -DVCPKG_TARGET_TRIPLET='${{ matrix.arch }}-${{ matrix.plat }}' \
              -DCMAKE_C_COMPILER_TARGET='${{ matrix.arch }}-${{ matrix.plat }}' \
              -DCMAKE_C_FLAGS="${cflags}" \
              -DCMAKE_EXE_LINKER_FLAGS="-INCREMENTAL:NO ${ldflags}" \
              -DCMAKE_SHARED_LINKER_FLAGS="-INCREMENTAL:NO ${ldflags}" \
              -DCMAKE_VS_GLOBALS="TrackFileAccess=false${vsglobals}" \
              -DCMAKE_UNITY_BUILD=ON -DCURL_TEST_BUNDLES=ON \
              -DCURL_WERROR=ON \
              -DBUILD_SHARED_LIBS=OFF \
              ${{ matrix.config }}
          done
          if [ -d bld_chkprefill ] && ! diff -u bld/lib/curl_config.h bld_chkprefill/lib/curl_config.h; then
            echo '::group::reference configure log'; cat bld_chkprefill/CMakeFiles/CMake*.yaml 2>/dev/null || true; echo '::endgroup::'
            false
          fi

      - name: 'configure log'
        if: ${{ !cancelled() }}
        run: cat bld/CMakeFiles/CMake*.yaml 2>/dev/null || true

      - name: 'curl_config.h'
        run: |
          echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::'
          grep -F '#define' bld/lib/curl_config.h | sort || true

      - name: 'build'
        timeout-minutes: 5
        run: cmake --build bld --config '${{ matrix.type }}' --parallel 5

      - name: 'curl version'
        timeout-minutes: 1
        run: |
          PATH=/usr/bin find . \( -name '*.exe' -o -name '*.dll' -o -name '*.lib' -o -name '*.pdb' \) -exec file '{}' \;
          if [ '${{ matrix.plat }}' != 'uwp' ]; then  # Missing: ucrtbased.dll, VCRUNTIME140D.dll, VCRUNTIME140D_APP.dll
            PATH="$PWD/bld/lib/${{ matrix.type }}:$PATH"
            'bld/src/${{ matrix.type }}/curl.exe' --disable --version
          fi

      - name: 'build tests'
        if: ${{ matrix.tflags != 'skipall' }}
        timeout-minutes: 10
        run: cmake --build bld --config '${{ matrix.type }}' --parallel 5 --target testdeps

      - name: 'install test prereqs'
        if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }}
        timeout-minutes: 5
        run: |
          if [ '${{ matrix.openssh }}' = '' ]; then  # MSYS2 openssh
            /usr/bin/pacman --noconfirm --noprogressbar --sync --needed openssh
          elif [ '${{ matrix.openssh }}' = 'OpenSSH-Windows-builtin' ]; then
            # https://learn.microsoft.com/windows-server/administration/openssh/openssh_install_firstuse
            if [ '${{ matrix.image }}' != 'windows-2025' ]; then
              pwsh -Command 'Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0'
              pwsh -Command 'Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0'
            fi
          else  # OpenSSH-Windows
            cd /c || exit 1  # no D: drive on windows-11-arm runners
            curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 \
              --location 'https://github.com/PowerShell/Win32-OpenSSH/releases/download/${{ env.openssh_windows-version }}/OpenSSH-Win64.zip' --output bin.zip
            unzip bin.zip
            rm -f bin.zip
          fi
          /c/ProgramData/chocolatey/choco.exe install --yes --no-progress --limit-output --timeout 180 --force stunnel || true
          if [ '${{ matrix.image }}' != 'windows-11-arm' ]; then  # save 30-60 seconds, to counteract the slower test run step
            python3 -m pip --disable-pip-version-check --no-input --no-cache-dir install --progress-bar off --prefer-binary impacket
          fi

      - name: 'downgrade msys2-runtime'
        if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }}
        timeout-minutes: 2
        # Downgrade to a known good MSYS2 runtime version to avoid the performance regression
        # causing runtests.pl to run at 2-3x reduced speed.
        run: |
          if [[ "$(uname -a)" = *' 3.5'* ]]; then
            exec /usr/bin/pacman --noconfirm --noprogressbar --upgrade https://mirror.msys2.org/msys/x86_64/msys2-runtime-3.5.4-8-x86_64.pkg.tar.zst
          fi

      - name: 'run tests'
        if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }}
        timeout-minutes: 10
        run: |
          export CURL_DIRSUFFIX='${{ matrix.type }}'
          export TFLAGS='-j8 ${{ matrix.tflags }}'
          TFLAGS+=' !498'  # 'Reject too large HTTP response headers on endless redirects' HTTP, HTTP GET (runtests detecting result code 2009 instead of 56 returned by curl)
          if [[ '${{ matrix.install }}' = *'libssh2[core,zlib]'* ]]; then
            TFLAGS+=' ~SCP ~SFTP'  # Flaky: `-8, Unable to exchange encryption keys`. https://github.com/libssh2/libssh2/issues/804
          fi
          if [ -n '${{ matrix.openssh }}' ]; then  # OpenSSH-Windows
            TFLAGS+=' ~601 ~603 ~617 ~619 ~621 ~641 ~665 ~2004'  # SCP
            if [[ '${{ matrix.install }}' = *'libssh '* ]]; then
              TFLAGS+=' ~614'  # 'SFTP pre-quote chmod' SFTP, pre-quote, directory
            else
              TFLAGS+=' ~3022'  # 'SCP correct sha256 host key' SCP, server sha256 key check
            fi
            PATH="/c/OpenSSH-Win64:$PATH"
          fi
          PATH="$PWD/bld/lib/${{ matrix.type }}:$PATH:/c/Program Files (x86)/stunnel/bin"
          cmake --build bld --config '${{ matrix.type }}' --target test-ci

      - name: 'build examples'
        timeout-minutes: 5
        if: ${{ contains(matrix.name, '+examples') }}
        run: cmake --build bld --config '${{ matrix.type }}' --parallel 5 --target curl-examples
