diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..e69de29 diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..543149e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +; http://editorconfig.org/ + +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +trim_trailing_whitespace = true \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..cd081dd --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,17 @@ +version: 2 +enable-beta-ecosystems: true +updates: + # Maintain dependencies for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "saturday" + commit-message: + prefix: "gh" + target-branch: master + - package-ecosystem: "docker" + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 \ No newline at end of file diff --git a/.github/workflows/action.yml b/.github/workflows/action.yml new file mode 100644 index 0000000..32978d2 --- /dev/null +++ b/.github/workflows/action.yml @@ -0,0 +1,27 @@ +name: ci + +on: + pull_request: + types: [opened, reopened, synchronize, ready_for_review] + push: + branches: [master] + paths-ignore: ["**.md"] + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.event.pull_request.head.ref || github.head_ref || github.ref }} + cancel-in-progress: true + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + heroku_stack_version: [24] + steps: + - name: ๐Ÿ  Checkout source code from repository + uses: actions/checkout@v4 + - name: ๐Ÿงช Build and run unit tests with Buildpack CI + uses: buildpack-ci/run-tests@v1 + env: + STACK: heroku-${{ matrix.heroku_stack_version }} + BUILDPACK_URL: https://github.com/yespark/heroku-imagemagick-buildpack#rework diff --git a/.gitignore b/.gitignore index 496ee2c..489cc46 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,10 @@ -.DS_Store \ No newline at end of file +### Linux ### +*~ + +### macOS ### +.DS_Store + +# Build artifacts +/build/* +!/build/configurations +!/build/heroku-*.tar.bz2 \ No newline at end of file diff --git a/.profile.d/imagemagick.sh b/.profile.d/imagemagick.sh new file mode 100644 index 0000000..a8caaa7 --- /dev/null +++ b/.profile.d/imagemagick.sh @@ -0,0 +1,3 @@ +export PATH="$PATH:$HOME/vendor/imagemagick/bin" +export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:$HOME/vendor/imagemagick/lib/pkgconfig" +export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$HOME/vendor/imagemagick/lib" diff --git a/README.md b/README.md index a5d80bc..9920c08 100644 --- a/README.md +++ b/README.md @@ -1,55 +1,89 @@ -# heroku-buildpack-imagemagick-heif +``` + _____ _ _ + \_ \_ __ ___ __ _ __ _ ___ /\/\ __ _ __ _(_) ___| | __ + / /\/ '_ ` _ \ / _` |/ _` |/ _ \/ \ / _` |/ _` | |/ __| |/ / + /\/ /_ | | | | | | (_| | (_| | __/ /\/\ \ (_| | (_| | | (__| < + \____/ |_| |_| |_|\__,_|\__, |\___\/ \/\__,_|\__, |_|\___|_|\_\ + |___/ |___/ +``` -The rise in popularity and use of HEIF/HEIC(High Efficency Image Format) means your project's image processing also needs to be able to handle this format. -The current default version of imagemagick installed on heroku:22 dynos is a version 6.xx and does not support processing heic image files. +# Heroku Buildpack for ImageMagick [![Build Status](https://github.com/yespark/heroku-imagemagick-buildpack/workflows/ci/badge.svg)](https://github.com/yespark/heroku-imagemagick-buildpack/actions?query=workflow%3Aci) -This [Heroku buildpack](https://devcenter.heroku.com/articles/buildpacks) vendors a version of ImageMagick with **WEBP and HEIF support** binaries into your project. +This [Heroku buildpack](https://devcenter.heroku.com/articles/buildpacks) includes a version of ImageMagick with **HEIF/HEIC/AVIF/WebP/TIFF support**. -This one works was built for [**Heroku stack 20**](https://devcenter.heroku.com/articles/stack). +With the rise in popularity of HEIF/HEIC/AVIF/WebP/TIFF formats, your project needs to handle this format efficiently. +The default version of ImageMagick installed on Heroku:22/24 dynos (version 7.xx) does not support processing HEIC and AVIF image files. -The tar file in the [/build folder](./build) currently contains: +| Software | Version | +|---------------|-------------| +| ImageMagick | 7.1.1-35 | +| libwebp | 1.4.0 | +| libheif | 1.18.1 | -Version: ImageMagick 7.1.0-53 +The binaries in this repository were built using Heroku Docker images with the [**build.sh**](./build.sh) script. -You will need to build a new binary if you want to use a newer or different version. To build a new binary see [How to Build a New Binary](#how-to-build-a-new-binary) +The resulting tar files are stored in the `/build` directory and utilized by the [compile script](./bin/compile). # Usage -## Step 1 : Adding the buildpack +It's suggested that you use the latest version of the release buildpack. + +To ensure the newer version of ImageMagick is found in the `$PATH` and installed first, make sure this buildpack is added to the top of the buildpack list (index 1). + +You can set it using the heroku-cli. -From your projects "Settings" tab add this buildpack to your app in the 1st position: +For linux/ubuntu: +```bash +curl https://cli-assets.heroku.com/install-ubuntu.sh | sh +``` +For mac: ```bash -https://github.com/yespark/heroku-imagemagick-buildpack +brew tap heroku/brew && brew install heroku ``` -**NOTE:** \__To ensure the newer version of imagemagick is found in the $PATH and installed first make sure this buildpack is added to the top of the buildpack list or at "index 1"._ +Then run: +```bash +heroku login +heroku buildpacks:set https://github.com/yespark/heroku-imagemagick-buildpack#rework โ€ฆ --index 1 -a heroku-imagemagick-dummy +``` -## Step 2 : Clear the cache(**Not Sure if this is necessary**) +# Notes -Since the installation is cached you might want to clean it out due to config changes. +If needed, clear the cache due to configuration changes: ```bash -heroku plugins:install heroku-builds -heroku builds:cache:purge -a HEROKU_APP_NAME +heroku plugins:install @heroku-cli/heroku-builds +heroku builds:cache:purge -a your-app-name ``` -# How to Build a New Binary (if you want to make somes changes) +## Workflow -The binary in this repo was built in a heroku:22 docker image running in a local dev environment. -However, there is a script called [**build.sh**](./build.sh) made to build a tar file through docker easily, it will be copied to the `build` directory. Then you should commit this changes to your git, and adjust the buildpack url previously mentionned just above. +This Dockerfile builds ImageMagick with updated libraries (libwebp, and libheif) on a Heroku stack. +Here's a breakdown of the process: -## Prerequisites +- Starts with a Heroku build image and sets up environment variables and arguments. +- Installs necessary dependencies like `pkg-config`, `ninja-build`, and `yasm`. +- Builds and installs the following libraries in order: + - `libwebp` + - `libheif` (including AOM as a dependency) + - `ImageMagick` +- For each library, it: + - Downloads the source code from GitHub + - Configures the build + - Compiles the library + - Installs it to `/usr/local` +- Cleans up unnecessary files and strips the shared libraries. +- Depending of the stack version, creates a tarball of the built binaries, libraries, and other necessary files. +- Updates the `ldconfig` cache to ensure the new shared libraries are recognized. +- Saves the ImageMagick configuration for reference. -- Docker installed and running in local dev environment. [Get Docker](https://docs.docker.com/get-docker/) +The result is a Docker image with updated versions of ImageMagick and its dependencies, with shared libraries (.so files) built and included in a tarball. -## Credits +## Contributing -- https://medium.com/@eplt/5-minutes-to-install-imagemagick-with-heic-support-on-ubuntu-18-04-digitalocean-fe2d09dcef1 -- https://github.com/brandoncc/heroku-buildpack-vips -- https://github.com/steeple-dev/heroku-buildpack-imagemagick -- https://github.com/retailzipline/heroku-buildpack-imagemagick-heif +Contributions to the plugin are welcome. Report bugs and suggestions using GitHub Issues on the repository. ## License -The gem is available as open source under the terms of the [MIT License](https://github.com/yespark/heroku-imagemagick-buildpack/blob/master/LICENSE). +This buildpack is available as open source under the terms of the [MIT License](https://github.com/yespark/heroku-imagemagick-buildpack/blob/master/LICENSE). diff --git a/bin/compile b/bin/compile old mode 100644 new mode 100755 index cc6431e..f55c528 --- a/bin/compile +++ b/bin/compile @@ -1,57 +1,96 @@ #!/usr/bin/env bash -# bin/compile -set -e +### Configure environment -BUILD_DIR=$1 -CACHE_DIR=$2 -ENV_DIR=$3 +set -o errexit # always exit on error +set -o pipefail # don't ignore exit codes when piping output +set -o nounset # fail on unset variables +unset GIT_DIR # Avoid GIT_DIR leak from previous build steps -BIN_DIR=$(cd "$(dirname "$0")"; pwd) +### Constants + +BINARY_NAME="${STACK}.tar.bz2" + +### Configure directories + +BUILD_DIR=${1:-} +CACHE_DIR=${2:-} +ENV_DIR=${3:-} +BP_DIR=$(cd $(dirname ${0:-}); cd ..; pwd) + +BIN_DIR=$(cd "$(dirname "$0:-")"; pwd) ROOT_DIR=$(dirname "$BIN_DIR") +function indent() { + c='s/^/ /' + case $(uname) in + Darwin) sed -l "$c";; + *) sed -u "$c";; + esac +} + +arrow() { + sed -u 's/^/-----> /' +} + +ADD_PATH="" + function vendor() { local binary="$1" local path="$2" + if [ ! -f "$binary" ]; then + echo "Binary file '${binary}' not found for stack ${STACK}" | indent + exit 1 + fi + + echo "Fetching $binary" | indent mkdir -p $path tar -xj -f $binary -C $path if [ -d "$path/bin" ]; then - export PATH="$path/bin:$PATH" + ADD_PATH="$path/bin" + else + ADD_PATH="" fi if [ -d "$path/lib/pkgconfig" ]; then - export PKG_CONFIG_PATH="$path/lib/pkgconfig:$PKG_CONFIG_PATH" + ADD_PKG_CONFIG_PATH="$path/lib/pkgconfig" + else + ADD_PKG_CONFIG_PATH="" fi - export CPPPATH="$path/include:$CPPPATH" - export CPATH="$path/include:$CPATH" - export LIBRARY_PATH="$path/lib:$LIBRARY_PATH" - export LD_LIBRARY_PATH="$path/lib:$LD_LIBRARY_PATH" + ADD_CPPPATH="$path/include" + ADD_CPATH="$path/include" + ADD_LIBRARY_PATH="$path/lib" + ADD_LD_LIBRARY_PATH="$path/lib" } -vendor "$ROOT_DIR/build/imagemagick.tar.bz2" "$BUILD_DIR/vendor/imagemagick" +echo "Vendoring binaries" | arrow +vendor "$ROOT_DIR/build/$BINARY_NAME" "$BUILD_DIR/vendor/imagemagick" +echo "Configuring build environment" | arrow cat < export export MAGICK_HOME="$BUILD_DIR/vendor/imagemagick" export MAGICK_CONFIGURE_PATH="/app/.magick" -export PATH="/app/bin:$PATH:\$PATH" -export LD_LIBRARY_PATH="\$LD_LIBRARY_PATH:$LD_LIBRARY_PATH" -export LIBRARY_PATH="\$LIBRARY_PATH:$LIBRARY_PATH" -export PKG_CONFIG_PATH="\$PKG_CONFIG_PATH:$PKG_CONFIG_PATH" -export CPPPATH="\$CPPPATH:$CPPPATH" -export CPATH="\$CPATH:$CPATH" +export PATH="/app/bin:$ADD_PATH:\$PATH" +export LD_LIBRARY_PATH="\$LD_LIBRARY_PATH:$ADD_LD_LIBRARY_PATH" +export LIBRARY_PATH="\$LIBRARY_PATH:$ADD_LIBRARY_PATH" +export PKG_CONFIG_PATH="\$PKG_CONFIG_PATH:$ADD_PKG_CONFIG_PATH" +export CPPPATH="\$CPPPATH:$ADD_CPPPATH" +export CPATH="\$CPATH:$ADD_CPATH" EOF +echo "Building runtime environment" | arrow mkdir -p $BUILD_DIR/.profile.d + cat < $BUILD_DIR/.profile.d/imagemagick.sh export MAGICK_HOME="$BUILD_DIR/vendor/imagemagick" export MAGICK_CONFIGURE_PATH="/app/.magick" -export PATH="/app/bin:${PATH//$BUILD_DIR//app}:\$PATH" -export LD_LIBRARY_PATH="\$LD_LIBRARY_PATH:${LD_LIBRARY_PATH//$BUILD_DIR//app}" -export LIBRARY_PATH="\$LIBRARY_PATH:${LIBRARY_PATH//$BUILD_DIR//app}" -export PKG_CONFIG_PATH="\$PKG_CONFIG_PATH:${PKG_CONFIG_PATH//$BUILD_DIR//app}" -export CPPPATH="\$CPPPATH:${CPPPATH//$BUILD_DIR//app}" -export CPATH="\$CPATH:${CPATH//$BUILD_DIR//app}" +export PATH="/app/bin:${ADD_PATH//$BUILD_DIR//app}:\$PATH" +export LD_LIBRARY_PATH="\$LD_LIBRARY_PATH:${ADD_LD_LIBRARY_PATH//$BUILD_DIR//app}" +export LIBRARY_PATH="\$LIBRARY_PATH:${ADD_LIBRARY_PATH//$BUILD_DIR//app}" +export PKG_CONFIG_PATH="\$PKG_CONFIG_PATH:${ADD_PKG_CONFIG_PATH//$BUILD_DIR//app}" +export CPPPATH="\$CPPPATH:${ADD_CPPPATH//$BUILD_DIR//app}" +export CPATH="\$CPATH:${ADD_CPATH//$BUILD_DIR//app}" EOF diff --git a/bin/detect b/bin/detect old mode 100644 new mode 100755 index 7a8f194..d2ff233 --- a/bin/detect +++ b/bin/detect @@ -1,2 +1,12 @@ #!/usr/bin/env bash -echo "ImageMagick" && exit 0 \ No newline at end of file + +SUPPORTED_STACKS=(heroku-22 heroku-24) + +for stack_option in "${SUPPORTED_STACKS[@]}"; do + if [[ $STACK = $stack_option ]]; then + echo "imagemagick ($stack_option stack)" + exit 0 + fi +done + +echo "imagemagick buildpack doesn't support the $STACK stack" && exit 1 diff --git a/bin/release b/bin/release old mode 100644 new mode 100755 diff --git a/bin/test b/bin/test new file mode 100755 index 0000000..bc9f6ba --- /dev/null +++ b/bin/test @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +cd "${1-}" + +function indent() { + c='s/^/ /' + case $(uname) in + Darwin) sed -l "$c";; + *) sed -u "$c";; + esac +} + +ls -lah /app/vendor/imagemagick/lib | indent + +magick -list configure | indent +# magick -resize 100 fixtures/test.avif fixtures/test-resize.avif | indent +# magick -resize 100 fixtures/test.heic fixtures/test-resize.heic | indent +# magick -resize 100 fixtures/test.jpg fixtures/test-resize.jpg | indent +# magick -resize 100 fixtures/test.jpg webp:fixtures/test-jpg.webp | indent +# magick -resize 100 fixtures/test.jpg avif:fixtures/test-jpg.avif | indent +# magick -resize 100 fixtures/test.png fixtures/test-resize.png | indent +# magick -resize 100 fixtures/test.png webp:fixtures/test-png.webp | indent +# magick -resize 100 fixtures/test.png avif:fixtures/test-png.avif | indent +# magick -resize 100 fixtures/test.png jxl:fixtures/test-png.jxl | indent +# magick -resize 100 fixtures/test.tiff fixtures/test-resize.tiff | indent +# magick -resize 100 fixtures/test.tiff webp:fixtures/test-tiff.webp | indent +# magick -resize 100 fixtures/test.tiff avif:fixtures/test-tiff.avif | indent +# magick -resize 100 fixtures/test.tiff jxl:fixtures/test-tiff.jxl | indent +# magick fixtures/jpg-with-metadata.jpg fixtures/jpg-with-metadata.webp | indent +# magick -coalesce fixtures/test.gif fixtures/test-gif.webp | indent +# magick fixtures/test.jpg -antialias -font DejaVu-Sans -pointsize 20 -gravity Southeast -annotate +15+15 'TEST' fixtures/test-text.jpg | indent diff --git a/bin/test-compile b/bin/test-compile new file mode 100755 index 0000000..4b4ab18 --- /dev/null +++ b/bin/test-compile @@ -0,0 +1,4 @@ +#!/bin/bash +# usage: bin/test-compile + +"$(dirname ${0:-})/compile" "$1" "$2" "$3" diff --git a/build.sh b/build.sh index d7dad2c..646c06a 100755 --- a/build.sh +++ b/build.sh @@ -1,5 +1,37 @@ #!/usr/bin/env bash -set -e -docker build --no-cache --pull -t heroku-imagemagick container -mkdir -p build -docker run --rm -t -v $PWD/build:/data heroku-imagemagick sh -c 'cp -f /usr/src/imagemagick/build/*.tar.bz2 /data/' + +set -o errexit # always exit on error +set -o pipefail # don't ignore exit codes when piping output +set -o nounset # fail on unset variables + +# Remove existing builds and configuration logs so that unsupported stacks are automatically removed +rm -rf ./build/*.tar.bz2 ./build/configurations/*.log 2> /dev/null || true +mkdir -p ./build/configurations + +# Stacks +STACK_VERSIONS=(24) + +# Loop through stack versions +for stack_version in "${STACK_VERSIONS[@]}"; do + image_name="heroku-imagemagick-stack-$stack_version" + + docker build \ + --no-cache \ + --platform linux/amd64 \ + --build-arg STACK_VERSION=${stack_version} \ + --tag $image_name \ + --progress=plain \ + --pull \ + --file container/Dockerfile \ + container + + mkdir -p build 2> /dev/null || true + + docker run \ + --platform linux/amd64 \ + --rm \ + -t \ + -v $PWD/build:/build \ + $image_name \ + sh -c "cp -f /usr/local/build/heroku-${stack_version}.tar.bz2 /build/ && cp -f /usr/local/build/heroku-${stack_version}.config.log /build/configurations" +done diff --git a/build/configurations/heroku-24.config.log b/build/configurations/heroku-24.config.log new file mode 100644 index 0000000..7a05398 --- /dev/null +++ b/build/configurations/heroku-24.config.log @@ -0,0 +1,55 @@ +$ magick -list configure + +Path: /usr/local/lib/ImageMagick-7.1.1//config-Q16HDRI/configure.xml + +Name Value +------------------------------------------------------------------------------- +CC gcc +CFLAGS -I/usr/include/libxml2 -I/usr/local/include -I/usr/local/include/webp -I/usr/local/include -I/usr/local/include/webp -I/usr/local/include -I/usr/local/include/webp -I/usr/include/libpng16 -I/usr/include/lqr-1 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/openjpeg-2.5 -I/usr/local/include -I/usr/local/include/webp -I/usr/include/freetype2 -I/usr/include/libpng16 -I/usr/include/freetype2 -I/usr/include/libpng16 -pthread -fopenmp -Wall -g -O2 -mtune=westmere -fexceptions -pthread -DMAGICKCORE_HDRI_ENABLE=1 -DMAGICKCORE_QUANTUM_DEPTH=16 -DMAGICKCORE_CHANNEL_MASK_DEPTH=32 +CHANNEL_MASK_DEPTH 32 +CODER_PATH /usr/local/lib/ImageMagick-7.1.1/modules-Q16HDRI/coders +CONFIGURE ./configure '--prefix=/usr/local' '--without-magick-plus-plus' '--disable-docs' '--disable-static' '--with-libdeflate=no' '--with-heic=yes' '--with-webp=yes' '--with-tiff=yes' '--with-x=no' '--with-zip=no' '--with-gvc=no' '--with-openexr=no' 'PKG_CONFIG_PATH=/usr/local/lib/pkgconfig' +CONFIGURE_PATH /usr/local/etc/ImageMagick-7/ +COPYRIGHT Copyright (C) 1999 ImageMagick Studio LLC +CPPFLAGS -I/usr/local/include/ImageMagick-7 +CXX g++ +CXXFLAGS -g -O2 -pthread +DEFS -DHAVE_CONFIG_H +DELEGATES bzlib djvu fontconfig freetype heic jbig jng jp2 jpeg lcms lqr lzma png tiff webp xml zlib zstd +DISTCHECK_CONFIG_FLAGS --disable-deprecated --with-quantum-depth=16 --with-jemalloc=no --with-umem=no --with-zip=no --with-autotrace=no --with-dps=no --with-fftw=no --with-flif=no --with-fpx=no --with-gslib=no --with-fontpath= --with-gvc=no --with-openexr=no --with-rsvg=no --with-uhdr=no --with-wmf=no --with-perl=no +DOCUMENTATION_PATH /usr/local/share/doc/ImageMagick-7 +EXEC-PREFIX /usr/local +EXECUTABLE_PATH /usr/local/bin +FEATURES Channel-masks(32-bit) Cipher DPC HDRI OpenMP +FILTER_PATH /usr/local/lib/ImageMagick-7.1.1/modules-Q16HDRI/filters +GIT_REVISION 21316 +HOST x86_64-pc-linux-gnu +INCLUDE_PATH /usr/local/include/ImageMagick-7 +LDFLAGS -L/usr/local/lib +LIB_VERSION 0x711 +LIB_VERSION_NUMBER 7,1,1,36 +LIBRARY_PATH /usr/local/lib/ImageMagick-7.1.1 +LIBS -ljbig -llcms2 -L/usr/local/lib -ltiff -lfreetype -ljpeg -llqr-1 -lglib-2.0 -lpng16 -ldjvulibre -lfontconfig -lfreetype -L/usr/local/lib -lheif -L/usr/local/lib -lwebpmux -lwebpdemux -L/usr/local/lib -lwebp -llzma -lbz2 -lopenjp2 -lxml2 -lz -lm -lpthread -lgomp +NAME ImageMagick +PCFLAGS -fopenmp -DMAGICKCORE_HDRI_ENABLE=1 -DMAGICKCORE_QUANTUM_DEPTH=16 -DMAGICKCORE_CHANNEL_MASK_DEPTH=32 +PREFIX /usr/local +QuantumDepth 16 +RELEASE_DATE 2024-07-26 +SECURITY_POLICY open +SHARE_PATH /usr/local/share/ImageMagick-7 +SHAREARCH_PATH /usr/local/lib/ImageMagick-7.1.1/config-Q16HDRI +TARGET_CPU x86_64 +TARGET_OS linux-gnu +TARGET_VENDOR pc +VERSION 7.1.1 +WEBSITE https://imagemagick.org + +Path: [built-in] + +Name Value +------------------------------------------------------------------------------- +DELEGATES bzlib djvu fontconfig freetype heic jbig jng jp2 jpeg lcms lqr lzma png tiff webp xml zlib zstd +FEATURES Cipher DPC HDRI OpenMP(4.5) +MAGICK_TEMPORARY_PATH /tmp +NAME ImageMagick +QuantumDepth Q16 diff --git a/build/heroku-24.tar.bz2 b/build/heroku-24.tar.bz2 new file mode 100644 index 0000000..9e47053 Binary files /dev/null and b/build/heroku-24.tar.bz2 differ diff --git a/build/imagemagick.tar.bz2 b/build/imagemagick.tar.bz2 deleted file mode 100644 index 4b9353e..0000000 Binary files a/build/imagemagick.tar.bz2 and /dev/null differ diff --git a/check.sh b/check.sh index 03ccc29..cf2294c 100755 --- a/check.sh +++ b/check.sh @@ -1,28 +1,34 @@ #!/usr/bin/env bash -set -e -COMPILE_VERSION_LIBDE265=$(cat container/Dockerfile | egrep -o 'COMPILE_VERSION_LIBDE265=([0-9.-]+)' | cut -f 2 -d =) -COMPILE_VERSION_LIBHEIF=$(cat container/Dockerfile | egrep -o 'COMPILE_VERSION_LIBHEIF=([0-9.-]+)' | cut -f 2 -d =) +set -o errexit # always exit on error +set -o pipefail # don't ignore exit codes when piping output +set -o nounset # fail on unset variables + COMPILE_VERSION_LIBWEBP=$(cat container/Dockerfile | egrep -o 'COMPILE_VERSION_LIBWEBP=([0-9.-]+)' | cut -f 2 -d =) +COMPILE_VERSION_LIBHEIF=$(cat container/Dockerfile | egrep -o 'COMPILE_VERSION_LIBHEIF=([0-9.-]+)' | cut -f 2 -d =) +COMPILE_VERSION_LIBTIFF=$(cat container/Dockerfile | egrep -o 'COMPILE_VERSION_LIBTIFF=([a-zA-Z0-9.-]+)' | cut -f 2 -d =) COMPILE_VERSION_IMAGEMAGICK=$(cat container/Dockerfile | egrep -o 'COMPILE_VERSION_IMAGEMAGICK=([0-9.-]+)' | cut -f 2 -d =) -LATEST_VERSION_LIBDE265=$(curl -s "https://api.github.com/repos/strukturag/libde265/releases/latest" | jq -r .tag_name | sed 's/v//') -LATEST_VERSION_LIBHEIF=$(curl -s "https://api.github.com/repos/strukturag/libheif/releases/latest" | jq -r .tag_name | sed 's/v//') -LATEST_VERSION_LIBWEBP=$(curl -s "https://api.github.com/repos/webmproject/libwebp/tags" | jq -r '.[0].name' | sed 's/v//') -LATEST_VERSION_IMAGEMAGICK=$(curl -s "https://api.github.com/repos/ImageMagick/ImageMagick/releases/latest" | jq -r .tag_name | sed 's/v//') +LATEST_VERSION_LIBWEBP=$(curl --retry-connrefused --retry 3 -ksSL "https://api.github.com/repos/webmproject/libwebp/tags" | jq -r '.[0].name' | sed 's/v//') +LATEST_VERSION_LIBWEBP=$(curl --retry-connrefused --retry 3 -ksSL "https://api.github.com/repos/webmproject/libwebp/tags" | jq -r '.[0].name' | sed 's/v//') +LATEST_VERSION_LIBHEIF=$(curl --retry-connrefused --retry 3 -ksSL "https://api.github.com/repos/strukturag/libheif/releases/latest" | jq -r .tag_name | sed 's/v//') +GITLAB_LIBTIFF_PROJECT_ID=$(curl --retry-connrefused --retry 3 -ksSL "https://gitlab.com/api/v4/projects?search=libtiff-tools" | jq -r '.[] | select(.path_with_namespace=="faxguy/libtiff-tools") | .id') +LATEST_VERSION_LIBTIFF=$(curl --retry-connrefused --retry 3 -ksSL "https://gitlab.com/api/v4/projects/$GITLAB_LIBTIFF_PROJECT_ID/repository/tags" | jq -r '.[0].name') +LATEST_VERSION_IMAGEMAGICK=$(curl --retry-connrefused --retry 3 -ksSL "https://api.github.com/repos/ImageMagick/ImageMagick/releases/latest" | jq -r .tag_name | sed 's/v//') -if [ "$COMPILE_VERSION_LIBDE265" != "$LATEST_VERSION_LIBDE265" ]; then - echo "[NEW UPDATE] COMPILE_VERSION_LIBDE265 $COMPILE_VERSION_LIBDE265 ๐Ÿ”„ $LATEST_VERSION_LIBDE265" -fi if [ "$COMPILE_VERSION_LIBHEIF" != "$LATEST_VERSION_LIBHEIF" ]; then - echo "[NEW UPDATE] COMPILE_VERSION_LIBHEIF $COMPILE_VERSION_LIBHEIF ๐Ÿ”„ $LATEST_VERSION_LIBHEIF" + echo -e "COMPILE_VERSION_LIBHEIF ($COMPILE_VERSION_LIBHEIF)\nNew version available: $LATEST_VERSION_LIBHEIF" fi if [ "$COMPILE_VERSION_LIBWEBP" != "$LATEST_VERSION_LIBWEBP" ]; then - echo "[NEW UPDATE] COMPILE_VERSION_LIBWEBP $COMPILE_VERSION_LIBWEBP ๐Ÿ”„ $LATEST_VERSION_LIBWEBP" + echo -e "COMPILE_VERSION_LIBWEBP ($COMPILE_VERSION_LIBWEBP)\nNew version available: $LATEST_VERSION_LIBWEBP" +fi + +if [ "$COMPILE_VERSION_LIBTIFF" != "$LATEST_VERSION_LIBTIFF" ]; then + echo -e "COMPILE_VERSION_LIBTIFF ($COMPILE_VERSION_LIBTIFF)\nNew version available: $LATEST_VERSION_LIBTIFF" fi if [ "$COMPILE_VERSION_IMAGEMAGICK" != "$LATEST_VERSION_IMAGEMAGICK" ]; then - echo "[NEW UPDATE] COMPILE_VERSION_IMAGEMAGICK $COMPILE_VERSION_IMAGEMAGICK ๐Ÿ”„ $LATEST_VERSION_IMAGEMAGICK" + echo -e "COMPILE_VERSION_IMAGEMAGICK ($COMPILE_VERSION_IMAGEMAGICK)\nNew version available: $LATEST_VERSION_IMAGEMAGICK" fi diff --git a/container/Dockerfile b/container/Dockerfile index 4db38cc..710619f 100644 --- a/container/Dockerfile +++ b/container/Dockerfile @@ -1,69 +1,100 @@ -FROM heroku/heroku:22-build +# syntax=docker/dockerfile-upstream:master-labs +ARG STACK_VERSION=24 +FROM heroku/heroku:${STACK_VERSION}-build -ENV DEBIAN_FRONTEND=noninteractive +# This ARG duplication is required since the lines before and after the 'FROM' are in different scopes. +ARG STACK_VERSION -ARG COMPILE_VERSION_LIBDE265=1.0.9 -ARG COMPILE_VERSION_LIBAOM=3.5.0 -ARG COMPILE_VERSION_LIBHEIF=1.14.0 -ARG COMPILE_VERSION_LIBWEBP=1.2.4 -ARG COMPILE_VERSION_IMAGEMAGICK=7.1.0-53 +# build to this prefix +# - heroku has /usr/local/lib on the default ld.so.conf search path, so this is convenient +# - heroku has a basic dir structure in /usr/local, but no files +ENV STACK="heroku-${STACK_VERSION}" \ + DEBIAN_FRONTEND=noninteractive \ + PREFIX=/usr/local \ + PKG_CONFIG_PATH=/usr/local/lib/pkgconfig -RUN apt-get -yq update \ - # Needed for libaom library cmake optimizations - && apt-get install -yq yasm \ - # Install the latest libde265 library - && curl -sSL --retry 3 https://github.com/strukturag/libde265/releases/download/v$COMPILE_VERSION_LIBDE265/libde265-$COMPILE_VERSION_LIBDE265.tar.gz | tar zx \ - && cd libde265-$COMPILE_VERSION_LIBDE265 \ - && ./autogen.sh \ - && ./configure \ - && make -j$(nproc) \ - && make install \ - && ldconfig /usr/local/lib \ - && cd .. \ - # Install the latest libwebp library - && curl -sSL --retry 3 http://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-$COMPILE_VERSION_LIBWEBP.tar.gz | tar zx \ - && cd libwebp-$COMPILE_VERSION_LIBWEBP \ - && ./autogen.sh \ - && ./configure --enable-shared --enable-libwebpdecoder --enable-libwebpdemux --enable-libwebpmux --enable-static=no \ - && make -j$(nproc) \ - && make install \ - && ldconfig /usr/local/lib \ +ARG COMPILE_VERSION_LIBWEBP=1.4.0 +ARG COMPILE_VERSION_LIBHEIF=1.18.1 +ARG COMPILE_VERSION_LIBTIFF=v4.6.0t +ARG COMPILE_VERSION_IMAGEMAGICK=7.1.1-36 + +USER root +WORKDIR /usr/local/src + +# Install dependencies (pkg-config/ninja-build is required for the compilation of libheif, yasm for cmake optimizations) +RUN set -ex \ + && apt-get -y -qq update \ + && apt-get install --no-install-recommends -y --allow-remove-essential pkg-config ninja-build yasm + +# Build and install libwebp +RUN set -ex \ +&& mkdir -p libwebp \ +&& cd libwebp \ +&& curl -sSL "https://api.github.com/repos/webmproject/libwebp/tarball/refs/tags/v${COMPILE_VERSION_LIBWEBP}" | tar zx --strip-components=1 \ +&& mkdir build \ +&& cd build \ +&& cmake -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=$PREFIX ../ \ +&& make -j$(nproc) \ +&& make install \ +&& ldconfig /usr/local/lib \ +&& rm -rf /usr/local/src/libwebp + +# Build and install libheif +RUN set -ex \ + && mkdir -p libheif \ + && cd libheif \ + && curl -sSL "https://api.github.com/repos/strukturag/libheif/tarball/v$COMPILE_VERSION_LIBHEIF" | tar zx --strip-components=1 \ + && cd third-party \ + && chmod +x aom.cmd \ + && ./aom.cmd \ + && export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$(pwd)/aom/dist/lib/pkgconfig \ && cd .. \ - # Install the latest libaom library - && git clone -b v${COMPILE_VERSION_LIBAOM} --depth 1 https://aomedia.googlesource.com/aom \ - && mkdir build_aom \ - && cd build_aom \ - && cmake ../aom/ -DENABLE_TESTS=0 -DBUILD_SHARED_LIBS=1 \ + && mkdir build \ + && cd build \ + && cmake --preset=release-noplugins -DCMAKE_INSTALL_PREFIX=$PREFIX -DWITH_EXAMPLES=OFF .. \ && make -j$(nproc) \ && make install \ - && ldconfig /usr/local/lib \ - && cd .. \ - # Install the latest libheif library - && curl -sSL --retry 3 https://github.com/strukturag/libheif/releases/download/v$COMPILE_VERSION_LIBHEIF/libheif-$COMPILE_VERSION_LIBHEIF.tar.gz | tar zx \ - && cd libheif-$COMPILE_VERSION_LIBHEIF \ - && ./autogen.sh \ - && ./configure \ + && ldconfig \ + && rm -rf /usr/local/src/libheif + +# Build and install libtiff +RUN set -ex \ + && mkdir -p libtiff \ + && cd libtiff \ + && curl -sSL "https://gitlab.com/faxguy/libtiff-tools/-/archive/$COMPILE_VERSION_LIBTIFF/libtiff-tools-$COMPILE_VERSION_LIBTIFF.tar.gz" | tar zx --strip-components=1 \ + && cd build \ + && cmake -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=$PREFIX ../ \ && make -j$(nproc) \ && make install \ - && ldconfig /usr/local/lib \ - && cd .. \ - # Get, configure and install ImageMagick - && curl -sSL --retry 3 https://github.com/ImageMagick/ImageMagick/archive/refs/tags/$COMPILE_VERSION_IMAGEMAGICK.tar.gz | tar zx \ - && cd ImageMagick-$COMPILE_VERSION_IMAGEMAGICK \ - && ./configure --prefix=/usr/src/imagemagick --disable-docs --with-avif=yes --with-heic=yes --with-webp=yes --with-tiff=yes --with-openexr=no --with-x=no \ + && ldconfig \ + && rm -rf /usr/local/src/libtiff + +# Build and install ImageMagick +RUN set -ex \ + && mkdir -p imagemagick \ + && cd imagemagick \ + && curl -sSL "https://api.github.com/repos/ImageMagick/ImageMagick/tarball/${COMPILE_VERSION_IMAGEMAGICK}" | tar zx --strip-components=1 \ + && ./configure --prefix=$PREFIX --without-magick-plus-plus --disable-docs --disable-static --with-libdeflate=no --with-heic=yes --with-webp=yes --with-tiff=yes --with-openexr=no --with-x=no --with-zip=no --with-gvc=no --with-openexr=no \ && make -j$(nproc) \ && make install \ - && ldconfig /usr/local/lib \ - && cd .. \ - # Copy shared libraries just compiled - && cp /usr/local/lib/libaom.so.3 /usr/src/imagemagick/lib \ - && cp /usr/local/lib/libde265.so.0 /usr/src/imagemagick/lib \ - && cp /usr/local/lib/libheif.so.1 /usr/src/imagemagick/lib \ - && cp /usr/local/lib/libwebp.so.7 /usr/src/imagemagick/lib \ - # Clean up the build and get ready for packaging - && cd /usr/src/imagemagick \ - && strip lib/*.a lib/lib*.so* \ - # Wrap it up with a bow (compress the binary) + && ldconfig + +# Update ldconfig cache +RUN ldconfig + +# Clean and package +RUN set -ex \ + && cd $PREFIX \ + && rm -rf lib/*.a lib/*.la share/gtk-doc share/man share/thumbnailer \ + && strip lib/lib*.so* \ && rm -rf build \ - && mkdir build \ - && tar cjf /usr/src/imagemagick/build/imagemagick.tar.bz2 bin include lib etc share + && mkdir -p build \ + && tar cjf "build/heroku-$STACK_VERSION.tar.bz2" bin include lib etc share + +# Store configuration for easy viewing in the repo +RUN set -ex \ + && cd $PREFIX \ + && echo "$ magick -list configure" > "build/heroku-$STACK_VERSION.config.log" \ + && magick -list configure >> "build/heroku-$STACK_VERSION.config.log" + +CMD ["tail", "-f", "/dev/null"] \ No newline at end of file diff --git a/fixtures/jpg-with-metadata.jpg b/fixtures/jpg-with-metadata.jpg new file mode 100644 index 0000000..7254403 Binary files /dev/null and b/fixtures/jpg-with-metadata.jpg differ diff --git a/fixtures/test-pdf.png b/fixtures/test-pdf.png new file mode 100755 index 0000000..cd7de33 Binary files /dev/null and b/fixtures/test-pdf.png differ diff --git a/fixtures/test-resize.jpg b/fixtures/test-resize.jpg new file mode 100755 index 0000000..38ade19 Binary files /dev/null and b/fixtures/test-resize.jpg differ diff --git a/fixtures/test.avif b/fixtures/test.avif new file mode 100644 index 0000000..a1c0abd Binary files /dev/null and b/fixtures/test.avif differ diff --git a/fixtures/test.gif b/fixtures/test.gif new file mode 100644 index 0000000..587eb50 Binary files /dev/null and b/fixtures/test.gif differ diff --git a/fixtures/test.heic b/fixtures/test.heic new file mode 100644 index 0000000..00cc549 Binary files /dev/null and b/fixtures/test.heic differ diff --git a/fixtures/test.jpg b/fixtures/test.jpg new file mode 100644 index 0000000..e3c56b3 Binary files /dev/null and b/fixtures/test.jpg differ diff --git a/fixtures/test.png b/fixtures/test.png new file mode 100644 index 0000000..0631dc5 Binary files /dev/null and b/fixtures/test.png differ diff --git a/fixtures/test.tiff b/fixtures/test.tiff new file mode 100644 index 0000000..a79cae6 Binary files /dev/null and b/fixtures/test.tiff differ