diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..95309dd --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,108 @@ +name: Build Nginx for Heroku Stacks + +on: + push: + branches: + - master + workflow_dispatch: + +jobs: + build20-22: + runs-on: ubuntu-latest + strategy: + matrix: + target: [heroku-20, heroku-22] + + steps: + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Run Makefile target + run: | + make build-${{ matrix.target }} + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: | + ${{ matrix.target }}.tgz + path: ./*.tgz + + build24: + runs-on: ubuntu-latest + strategy: + matrix: + target: [heroku-24] + arch: [amd64, arm64] + + steps: + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: all + + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Run Makefile target + run: | + make build-${{ matrix.target }}-${{ matrix.arch }} + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: | + ${{ matrix.target }}-${{ matrix.arch }}.tgz + path: ./*.tgz + + + commit: + runs-on: ubuntu-latest + needs: [build24, build20-22] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Download build artifacts + run: ls -R + - uses: actions/download-artifact@v4 + with: + path: ./ + + - name: Move artifacts to root directory + run: | + ls -R + mv ./heroku*/*.tgz . + ls -R + + - name: Remove folders starting with "heroku" + run: | + find . -type d -name "heroku*" -exec rm -rf {} + + ls -R + + - name: Commit and push artifacts + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + ls -R + git config --local user.email "actions@github.com" + git config --local user.name "GitHub Action" + git add *.tgz + git commit -m "Add nginx builds for Heroku stacks on multiple architectures" + git push diff --git a/Makefile b/Makefile index 411feac..4797c9f 100644 --- a/Makefile +++ b/Makefile @@ -1,16 +1,22 @@ -build: build-heroku-18 build-heroku-20 build-heroku-22 - -build-heroku-18: - @echo "Building nginx in Docker for heroku-18..." - @docker run -v $(shell pwd):/buildpack --rm -it -e "STACK=heroku-18" -w /buildpack heroku/heroku:18-build scripts/build_nginx /buildpack/nginx-heroku-18.tgz +build: build-heroku-20 build-heroku-22 build-heroku-24 build-heroku-20: @echo "Building nginx in Docker for heroku-20..." - @docker run -v $(shell pwd):/buildpack --rm -it -e "STACK=heroku-20" -w /buildpack heroku/heroku:20-build scripts/build_nginx /buildpack/nginx-heroku-20.tgz + @docker run -v $(shell pwd):/buildpack --rm -e "STACK=heroku-20" -w /buildpack heroku/heroku:20-build scripts/build_nginx /buildpack/nginx-heroku-20.tgz build-heroku-22: @echo "Building nginx in Docker for heroku-22..." - @docker run -v $(shell pwd):/buildpack --rm -it -e "STACK=heroku-22" -w /buildpack heroku/heroku:22-build scripts/build_nginx /buildpack/nginx-heroku-22.tgz + @docker run -v $(shell pwd):/buildpack --rm -e "STACK=heroku-22" -w /buildpack heroku/heroku:22-build scripts/build_nginx /buildpack/nginx-heroku-22.tgz + +build-heroku-24: build-heroku-24-amd64 build-heroku-24-arm64 + +build-heroku-24-amd64: + @echo "Building nginx in Docker for heroku-24 (amd64)..." + @docker run -v $(shell pwd):/buildpack --rm --user root -e "STACK=heroku-24" -w /buildpack --platform linux/amd64 heroku/heroku:24-build scripts/build_nginx /buildpack/nginx-heroku-24-amd64.tgz + +build-heroku-24-arm64: + @echo "Building nginx in Docker for heroku-24 (arm64)..." + @docker run -v $(shell pwd):/buildpack --rm --user root -e "STACK=heroku-24" -w /buildpack --platform linux/arm64 heroku/heroku:24-build scripts/build_nginx /buildpack/nginx-heroku-24-arm64.tgz shell: @echo "Opening heroku-22 shell..." diff --git a/bin/compile b/bin/compile index 8c416ca..a16aa3a 100755 --- a/bin/compile +++ b/bin/compile @@ -11,14 +11,23 @@ CACHE_DIR=$2 BUILDPACK_DIR="$(dirname "$(dirname "$0")")" GEO_IP_DIR="$BUILD_DIR/.geoip" +ruby_version="3.2.4" +if [[ $STACK == heroku-2[02] ]]; then + nginx_tarball=nginx-${STACK}.tgz + ruby_tarball=${STACK}/ruby-${ruby_version}.tgz +else + nginx_tarball=nginx-$STACK-$(dpkg --print-architecture).tgz + ruby_tarball=${STACK}/$(dpkg --print-architecture)/ruby-${ruby_version}.tgz +fi + mkdir -p $GEO_IP_DIR mkdir -p "$BUILD_DIR/bin/" -mkdir -p "$BUILD_DIR/nginx" -tar -zxvf "nginx-$STACK".tgz -C "$BUILD_DIR/nginx" +nginx_tmp=$(mktemp -d -t nginx.XXXXXXXXXX) +tar -zxvf "${nginx_tarball}" -C "$nginx_tmp" -cp "$BUILD_DIR/nginx/nginx" "$BUILD_DIR/bin/nginx" -cp "$BUILD_DIR/nginx/nginx-debug" "$BUILD_DIR/bin/nginx-debug" -cp -r "$BUILD_DIR/nginx/maxmind/." $GEO_IP_DIR +mv "$nginx_tmp/nginx" "$BUILD_DIR/bin/nginx" +mv "$nginx_tmp/nginx-debug" "$BUILD_DIR/bin/nginx-debug" +cp -r "$nginx_tmp/maxmind/." $GEO_IP_DIR mkdir -p $BUILD_DIR/.profile.d cat <$BUILD_DIR/.profile.d/geoip.sh @@ -35,9 +44,8 @@ echo "-----> nginx-buildpack: Installed ${nginx_version} to app/bin" # app doesn't already have the Ruby buildpack set before this one, we have to vendor # our own copy of Ruby and ensure it's on PATH at runtime. if ! command -v erb &> /dev/null; then - echo "-----> nginx-buildpack: An existing Ruby installation was not found (required for erb template support)" - ruby_version="3.1.2" - ruby_url="https://heroku-buildpack-ruby.s3.us-east-1.amazonaws.com/${STACK}/ruby-${ruby_version}.tgz" + echo "-----> nginx-buildpack: An existing Ruby installation was not found (required for erb template support)" + ruby_url="https://heroku-buildpack-ruby.s3.us-east-1.amazonaws.com/${ruby_tarball}" vendored_ruby_dir=".heroku-buildpack-nginx/ruby" mkdir -p "${BUILD_DIR}/${vendored_ruby_dir}" @@ -64,7 +72,7 @@ echo '-----> nginx-buildpack: Added start-nginx-solo to app/bin' mkdir -p "$BUILD_DIR/config" if [[ ! -f $BUILD_DIR/config/mime.types ]]; then - cp "$BUILD_DIR/nginx/mime.types" "$BUILD_DIR/config/" + mv "$nginx_tmp/mime.types" "$BUILD_DIR/config/" echo '-----> nginx-buildpack: Default mime.types copied to app/config/' else echo '-----> nginx-buildpack: Custom mime.types found in app/config.' @@ -78,6 +86,6 @@ else fi # cleanup -rm -r "$BUILD_DIR/nginx" +rm -r "$nginx_tmp" exit 0 diff --git a/nginx-heroku-18.tgz b/nginx-heroku-18.tgz deleted file mode 100644 index e02976a..0000000 Binary files a/nginx-heroku-18.tgz and /dev/null differ diff --git a/nginx-heroku-20.tgz b/nginx-heroku-20.tgz deleted file mode 100644 index 0097f7e..0000000 Binary files a/nginx-heroku-20.tgz and /dev/null differ diff --git a/nginx-heroku-22.tgz b/nginx-heroku-22.tgz deleted file mode 100644 index 3f08bdc..0000000 Binary files a/nginx-heroku-22.tgz and /dev/null differ diff --git a/scripts/build_nginx b/scripts/build_nginx index 295b378..1ead7c8 100755 --- a/scripts/build_nginx +++ b/scripts/build_nginx @@ -5,22 +5,37 @@ # at https://devcenter.heroku.com/articles/stack GEO_IP2_VERSION=${GEO_IP2_VERSION-3.4} -HEADERS_MORE_VERSION=${HEADERS_MORE_VERSION-0.33} -MAXMIND_VERSION=${MAXMIND_VERSION-1.6.0} -NGINX_VERSION=${NGINX_VERSION-1.20.2} -PCRE_VERSION=${PCRE_VERSION-8.45} +HEADERS_MORE_VERSION=${HEADERS_MORE_VERSION-0.37} +MAXMIND_VERSION=${MAXMIND_VERSION-1.11.0} +NGINX_VERSION=${NGINX_VERSION-1.26.2} +#PCRE_VERSION=${PCRE_VERSION-8.45} UUID4_VERSION=${UUID4_VERSION-master} -ZLIB_VERSION=${ZLIB_VERSION-1.2.12} +ZLIB_VERSION=${ZLIB_VERSION-1.3.1} +ACCEPT_LANGUAGE_VERSION=${ACCEPT_LANGUAGE_VERSION-master} + +# Set STACK variable (or get it from an environment variable) +STACK=${STACK:-heroku-20} # Default to heroku-20 if STACK is not set + +# Determine PCRE_VERSION based on STACK value +if [[ "$STACK" =~ heroku-2[02] ]]; then + PCRE_VERSION="pcre-8.45" +else + PCRE_VERSION="pcre2-10.37" +fi + geo_ip2_module_url=https://github.com/leev/ngx_http_geoip2_module/archive/${GEO_IP2_VERSION}.tar.gz headers_more_nginx_module_url=https://github.com/openresty/headers-more-nginx-module/archive/v${HEADERS_MORE_VERSION}.tar.gz maxmind_url=https://github.com/maxmind/libmaxminddb/releases/download/${MAXMIND_VERSION}/libmaxminddb-${MAXMIND_VERSION}.tar.gz nginx_tarball_url=https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz -pcre_tarball_url=https://ftp.exim.org/pub/pcre/pcre-${PCRE_VERSION}.tar.gz +pcre_tarball_url=https://ftp.exim.org/pub/pcre/${PCRE_VERSION}.tar.gz uuid4_url=https://github.com/cybozu/nginx-uuid4-module/archive/${UUID4_VERSION}.tar.gz zlib_url=http://zlib.net/zlib-${ZLIB_VERSION}.tar.gz +accept_language_url=https://github.com/giom/nginx_accept_language_module/archive/${ACCEPT_LANGUAGE_VERSION}.tar.gz + temp_dir=$(mktemp -d /tmp/nginx.XXXXXXXXXX) +maxmind_dir=$(mktemp -d /tmp/maxmind.XXXXXXXXXX) cd $temp_dir echo "Temp dir: $temp_dir" @@ -46,29 +61,44 @@ echo "Downloading $uuid4_url" echo "Downloading $zlib_url" (cd nginx-${NGINX_VERSION} && curl -L $zlib_url | tar xvz ) +echo "Downloading $accept_language_url" +(cd nginx-${NGINX_VERSION} && curl -L $accept_language_url | tar xvz ) + # This will build libmaxminddb first which is required for GeoIP2 ( cd nginx-${NGINX_VERSION}/libmaxminddb-${MAXMIND_VERSION} - ./configure + ./configure \ + --prefix=${maxmind_dir} make make check make install ldconfig ) +if [[ $STACK == heroku-2[02] ]]; then + # we used to build our own PCRE 8.x, and when moving to dynamic linking, we had to ensure all existing regexes in config files continued to work, so we enforced libpcre3 (8.x) usage instead of the newer PCRE2 (10.x), which has stricter validation for certain patterns (example: /[\w-.]/ is not allowed in PCRE2) + # but for any newer stacks, we can use the more modern PCRE2 + configure_opts+=( + --without-pcre2 + ) +fi + # This will build `nginx` ( cd nginx-${NGINX_VERSION} ./configure \ - --with-pcre=pcre-${PCRE_VERSION} \ + --with-pcre=${PCRE_VERSION} \ --with-zlib=zlib-${ZLIB_VERSION} \ --with-http_gzip_static_module \ --with-http_realip_module \ --with-http_ssl_module \ + --with-cc-opt="-I${maxmind_dir}/include" \ + --with-ld-opt="-L${maxmind_dir}/lib" \ --prefix=/tmp/nginx \ --add-module=${temp_dir}/nginx-${NGINX_VERSION}/headers-more-nginx-module-${HEADERS_MORE_VERSION} \ --add-module=${temp_dir}/nginx-${NGINX_VERSION}/nginx-uuid4-module-${UUID4_VERSION} \ - --add-module=${temp_dir}/nginx-${NGINX_VERSION}/ngx_http_geoip2_module-${GEO_IP2_VERSION} + --add-module=${temp_dir}/nginx-${NGINX_VERSION}/ngx_http_geoip2_module-${GEO_IP2_VERSION} \ + --add-module=${temp_dir}/nginx-${NGINX_VERSION}/nginx_accept_language_module-${ACCEPT_LANGUAGE_VERSION} make install ) @@ -77,24 +107,30 @@ echo "Downloading $zlib_url" cd nginx-${NGINX_VERSION} ./configure \ --with-debug \ - --with-pcre=pcre-${PCRE_VERSION} \ + --with-pcre=${PCRE_VERSION} \ --with-zlib=zlib-${ZLIB_VERSION} \ --with-http_gzip_static_module \ --with-http_realip_module \ --with-http_ssl_module \ + --with-cc-opt="-I${maxmind_dir}/include" \ + --with-ld-opt="-L${maxmind_dir}/lib" \ --prefix=/tmp/nginx-debug \ --add-module=${temp_dir}/nginx-${NGINX_VERSION}/headers-more-nginx-module-${HEADERS_MORE_VERSION} \ --add-module=${temp_dir}/nginx-${NGINX_VERSION}/nginx-uuid4-module-${UUID4_VERSION} \ - --add-module=${temp_dir}/nginx-${NGINX_VERSION}/ngx_http_geoip2_module-${GEO_IP2_VERSION} + --add-module=${temp_dir}/nginx-${NGINX_VERSION}/ngx_http_geoip2_module-${GEO_IP2_VERSION} \ + --add-module=${temp_dir}/nginx-${NGINX_VERSION}/nginx_accept_language_module-${ACCEPT_LANGUAGE_VERSION} make install ) release_dir=$(mktemp -d /tmp/nginx.XXXXXXXXXX) -cp /tmp/nginx/sbin/nginx $release_dir/nginx -cp /tmp/nginx-debug/sbin/nginx $release_dir/nginx-debug -cp /tmp/nginx/conf/mime.types $release_dir/mime.types +mv /tmp/nginx/sbin/nginx "$release_dir/nginx" +mv /tmp/nginx-debug/sbin/nginx "$release_dir/nginx-debug" +mv /tmp/nginx/conf/mime.types "$release_dir/mime.types" mkdir $release_dir/maxmind -cp -r /usr/local/lib/libmaxminddb* $release_dir/maxmind -tar -zcvf /tmp/nginx-"${STACK}".tgz -C $release_dir . -cp /tmp/nginx-"${STACK}".tgz $1 +mv ${maxmind_dir}/lib/* $release_dir/maxmind/ +echo "File: $1" +tar -zcvf "/tmp/release.tgz" -C "$release_dir" . +mv "/tmp/release.tgz" "$1" + +