Skip to content
Closed
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
356 changes: 356 additions & 0 deletions .github/workflows/build-curl-h3.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,356 @@
name: Build curl HTTP/3 prebuild

on:
push:
tags:
- 'v*'

permissions:
contents: write

concurrency:
group: build-curl-h3-${{ github.ref }}
cancel-in-progress: true

env:
CURL_VERSION: 8.20.0
CURL_SHA256: 63fe2dc148ba0ceae89922ef838f7e5c946272c2e78b7c59fab4b79d3ce2b896
ZLIB_VERSION: 1.3.2
ZLIB_SHA256: bb329a0a2cd0274d05519d61c667c062e06990d72e125ee2dfa8de64f0119d16
OPENSSL_VERSION: 3.6.2
OPENSSL_SHA256: aaf51a1fe064384f811daeaeb4ec4dce7340ec8bd893027eee676af31e83a04f
NGHTTP2_VERSION: 1.69.0
NGHTTP2_SHA256: 1fb324b6ec2c56f6bde0658f4139ffd8209fa9e77ce98fd7a5f63af8d0e508ad
NGHTTP3_VERSION: 1.16.0
NGHTTP3_SHA256: 776f59a99905c9a348846807b2e5ac9bb3485fc0f8c0250ba803018d5238a16e
NGTCP2_VERSION: 1.23.0
NGTCP2_SHA256: 59d5b4211e96970f2d3d5e6876f73dce03414800ba04aa56835b132fce8de730
CURL_H3_PREFIX: /opt/curl-h3

jobs:
build:
name: Build curl-h3 tarball
runs-on: ubuntu-24.04
container:
image: ubuntu:24.04
defaults:
run:
shell: bash

steps:
- name: Install build tools
env:
DEBIAN_FRONTEND: noninteractive
run: |
set -euo pipefail

apt-get update
apt-get install -y --no-install-recommends \
autoconf \
automake \
build-essential \
ca-certificates \
coreutils \
curl \
file \
git \
gnupg \
libtool \
make \
perl \
pkg-config \
tar \
wget \
xz-utils

install -m 0755 -d /etc/apt/keyrings
wget -qO- https://cli.github.com/packages/githubcli-archive-keyring.gpg \
| gpg --dearmor -o /etc/apt/keyrings/githubcli-archive-keyring.gpg
chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
> /etc/apt/sources.list.d/github-cli.list
apt-get update
apt-get install -y --no-install-recommends gh

- name: Resolve build inputs
id: vars
env:
PUSH_TAG: ${{ github.ref_name }}
run: |
set -euo pipefail

validate_release_tag() {
local value=$1

if [[ "$value" == -* ]] || [[ ! "$value" =~ ^[0-9A-Za-z._/@+-]+$ ]] || [[ "$value" == *..* ]] || [[ "$value" == *//* ]] || ! git check-ref-format "refs/tags/$value" >/dev/null 2>&1; then
echo "Invalid release tag: $value" >&2
exit 1
fi
}

release_tag="$PUSH_TAG"
if [ -z "$release_tag" ]; then
echo "release_tag is required" >&2
exit 1
fi
validate_release_tag "$release_tag"

release_target="$GITHUB_SHA"

asset_name="curl-h3-curl-${CURL_VERSION}-zlib-${ZLIB_VERSION}-openssl-${OPENSSL_VERSION}-nghttp2-${NGHTTP2_VERSION}-nghttp3-${NGHTTP3_VERSION}-ngtcp2-${NGTCP2_VERSION}-$(uname -m).tar.gz"

{
echo "release_tag=$release_tag"
echo "release_target=$release_target"
echo "asset_name=$asset_name"
} >> "$GITHUB_OUTPUT"

- name: Build curl with HTTP/3
id: build
run: |
set -euo pipefail

download_and_verify() {
local url=$1
local file=$2
local sha256=$3

echo "Downloading $url"
curl --fail --silent --show-error --location --proto '=https' --tlsv1.2 \
"$url" \
--output "$file"
printf '%s %s\n' "$sha256" "$file" | sha256sum -c -
}

workdir="$GITHUB_WORKSPACE/.build/curl-h3"
prefix="$CURL_H3_PREFIX"
mkdir -p "$workdir" "$prefix"

export PKG_CONFIG_PATH="$prefix/zlib/lib/pkgconfig:$prefix/openssl/lib/pkgconfig:$prefix/nghttp2/lib/pkgconfig:$prefix/nghttp3/lib/pkgconfig:$prefix/ngtcp2/lib/pkgconfig"
export PKG_CONFIG="pkg-config --static"
static_ldflags="-L$prefix/lib -L$prefix/zlib/lib -L$prefix/openssl/lib -L$prefix/nghttp2/lib -L$prefix/nghttp3/lib -L$prefix/ngtcp2/lib"
jobs="$(nproc)"

cd "$workdir"

download_and_verify \
"https://github.com/madler/zlib/releases/download/v${ZLIB_VERSION}/zlib-${ZLIB_VERSION}.tar.gz" \
"zlib-${ZLIB_VERSION}.tar.gz" \
"$ZLIB_SHA256"
tar -xf "zlib-${ZLIB_VERSION}.tar.gz"
cd "zlib-${ZLIB_VERSION}"
./configure --static --prefix="$prefix/zlib"
make -j"$jobs"
make install

cd "$workdir"
download_and_verify \
"https://github.com/openssl/openssl/releases/download/openssl-${OPENSSL_VERSION}/openssl-${OPENSSL_VERSION}.tar.gz" \
"openssl-${OPENSSL_VERSION}.tar.gz" \
"$OPENSSL_SHA256"
tar -xf "openssl-${OPENSSL_VERSION}.tar.gz"
cd "openssl-${OPENSSL_VERSION}"
./config --prefix="$prefix/openssl" --openssldir=/etc/ssl --libdir=lib no-shared no-module no-tests
make -j"$jobs"
make install_sw

cd "$workdir"
download_and_verify \
"https://github.com/nghttp2/nghttp2/releases/download/v${NGHTTP2_VERSION}/nghttp2-${NGHTTP2_VERSION}.tar.xz" \
"nghttp2-${NGHTTP2_VERSION}.tar.xz" \
"$NGHTTP2_SHA256"
tar -xf "nghttp2-${NGHTTP2_VERSION}.tar.xz"
cd "nghttp2-${NGHTTP2_VERSION}"
./configure --prefix="$prefix/nghttp2" --enable-lib-only --disable-shared --enable-static
make -j"$jobs"
make install

cd "$workdir"
download_and_verify \
"https://github.com/ngtcp2/nghttp3/releases/download/v${NGHTTP3_VERSION}/nghttp3-${NGHTTP3_VERSION}.tar.xz" \
"nghttp3-${NGHTTP3_VERSION}.tar.xz" \
"$NGHTTP3_SHA256"
tar -xf "nghttp3-${NGHTTP3_VERSION}.tar.xz"
cd "nghttp3-${NGHTTP3_VERSION}"
./configure --prefix="$prefix/nghttp3" --enable-lib-only --disable-shared --enable-static
make -j"$jobs"
make install

cd "$workdir"
download_and_verify \
"https://github.com/ngtcp2/ngtcp2/releases/download/v${NGTCP2_VERSION}/ngtcp2-${NGTCP2_VERSION}.tar.xz" \
"ngtcp2-${NGTCP2_VERSION}.tar.xz" \
"$NGTCP2_SHA256"
tar -xf "ngtcp2-${NGTCP2_VERSION}.tar.xz"
cd "ngtcp2-${NGTCP2_VERSION}"
./configure \
PKG_CONFIG="$PKG_CONFIG" \
PKG_CONFIG_PATH="$PKG_CONFIG_PATH" \
LDFLAGS="$static_ldflags" \
LIBS="-ldl -pthread" \
--prefix="$prefix/ngtcp2" \
--enable-lib-only \
--disable-shared \
--enable-static \
--with-openssl="$prefix/openssl"
make -j"$jobs"
make install

cd "$workdir"
download_and_verify \
"https://curl.se/download/curl-${CURL_VERSION}.tar.xz" \
"curl-${CURL_VERSION}.tar.xz" \
"$CURL_SHA256"
tar -xf "curl-${CURL_VERSION}.tar.xz"
cd "curl-${CURL_VERSION}"
./configure \
PKG_CONFIG="$PKG_CONFIG" \
PKG_CONFIG_PATH="$PKG_CONFIG_PATH" \
CPPFLAGS="-I$prefix/zlib/include" \
LDFLAGS="$static_ldflags" \
LIBS="-ldl -pthread" \
--prefix="$prefix" \
--disable-shared \
--enable-static \
--with-openssl="$prefix/openssl" \
--with-zlib="$prefix/zlib" \
--with-nghttp2="$prefix/nghttp2" \
--with-ngtcp2="$prefix/ngtcp2" \
--with-nghttp3="$prefix/nghttp3" \
--enable-threaded-resolver \
--enable-alt-svc \
--enable-hsts \
--enable-rtsp \
--enable-smb \
--enable-ntlm \
--enable-mqtt \
--enable-tls-srp \
--enable-unix-sockets \
--disable-ipfs \
--disable-websockets \
--disable-ldap \
--disable-ldaps \
--disable-ares \
--without-brotli \
--without-zstd \
--without-libidn2 \
--without-libpsl \
--without-libssh2 \
--without-libssh \
--without-gssapi \
--without-libgsasl
make -j"$jobs"
make install

"$prefix/bin/curl" --version | tee "$GITHUB_WORKSPACE/curl-version.txt"
grep -q 'HTTP3' "$GITHUB_WORKSPACE/curl-version.txt"
ldd "$prefix/bin/curl" | tee "$GITHUB_WORKSPACE/curl-ldd.txt"
if grep -q 'not found' "$GITHUB_WORKSPACE/curl-ldd.txt"; then
echo "curl has unresolved shared library dependencies" >&2
exit 1
fi
while read -r dependency _; do
case "$dependency" in
linux-vdso.so.1|libc.so.6|/lib64/ld-linux-x86-64.so.2) ;;
*)
echo "curl has unexpected dynamic dependency: $dependency" >&2
exit 1
;;
esac
done < "$GITHUB_WORKSPACE/curl-ldd.txt"

- name: Package prebuild
id: package
env:
ASSET_NAME: ${{ steps.vars.outputs.asset_name }}
run: |
set -euo pipefail

artifact="$GITHUB_WORKSPACE/$ASSET_NAME"
prefix="$CURL_H3_PREFIX"
runtime="$GITHUB_WORKSPACE/curl-h3-runtime"

rm -rf "$runtime"
mkdir -p "$runtime/bin"

cp -a "$prefix/bin/curl" "$runtime/bin/curl"
strip --strip-unneeded "$runtime/bin/curl"

"$runtime/bin/curl" -h >/dev/null
"$runtime/bin/curl" --version | tee "$GITHUB_WORKSPACE/runtime-curl-version.txt"
grep -q 'HTTP3' "$GITHUB_WORKSPACE/runtime-curl-version.txt"
ldd "$runtime/bin/curl" | tee "$GITHUB_WORKSPACE/runtime-curl-ldd.txt"
if grep -q 'not found' "$GITHUB_WORKSPACE/runtime-curl-ldd.txt"; then
echo "runtime curl has unresolved shared library dependencies" >&2
exit 1
fi
while read -r dependency _; do
case "$dependency" in
linux-vdso.so.1|libc.so.6|/lib64/ld-linux-x86-64.so.2) ;;
*)
echo "runtime curl has unexpected dynamic dependency: $dependency" >&2
exit 1
;;
esac
done < "$GITHUB_WORKSPACE/runtime-curl-ldd.txt"

tar -czf "$artifact" -C "$runtime" bin/curl
tar -tzf "$artifact" > "$GITHUB_WORKSPACE/tar-contents.txt"
grep -qx 'bin/curl' "$GITHUB_WORKSPACE/tar-contents.txt"
contents_count="$(wc -l < "$GITHUB_WORKSPACE/tar-contents.txt")"
if [ "$contents_count" -ne 1 ]; then
echo "runtime package must contain only bin/curl" >&2
exit 1
fi

{
echo "asset_path=$artifact"
echo "asset_name=$ASSET_NAME"
} >> "$GITHUB_OUTPUT"

- name: Ensure release exists
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ steps.vars.outputs.release_tag }}
RELEASE_TARGET: ${{ steps.vars.outputs.release_target }}
run: |
set -euo pipefail

gh api "repos/$GITHUB_REPOSITORY/commits/$RELEASE_TARGET" >/dev/null

if gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
echo "Release $RELEASE_TAG already exists"
else
gh release create "$RELEASE_TAG" \
--repo "$GITHUB_REPOSITORY" \
--target "$RELEASE_TARGET" \
--title "$RELEASE_TAG" \
--notes "curl-h3 prebuild for curl ${CURL_VERSION}"
fi

- name: Upload release asset
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ steps.vars.outputs.release_tag }}
ASSET_PATH: ${{ steps.package.outputs.asset_path }}
run: |
set -euo pipefail

gh release upload "$RELEASE_TAG" "$ASSET_PATH" --repo "$GITHUB_REPOSITORY" --clobber

- name: Write build summary
env:
RELEASE_TAG_SUMMARY: ${{ steps.vars.outputs.release_tag }}
ASSET_NAME_SUMMARY: ${{ steps.vars.outputs.asset_name }}
run: |
{
echo "### curl-h3 prebuild"
echo
echo "- Release tag: \`$RELEASE_TAG_SUMMARY\`"
echo "- Asset: \`$ASSET_NAME_SUMMARY\`"
echo "- Prefix: \`$CURL_H3_PREFIX\`"
echo
echo '```text'
cat "$GITHUB_WORKSPACE/curl-version.txt"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"