# This is not a valid Docker Compose file. It contains examples for containers
# to be used with presets from MediaWiki extension External Data.

services:

  # MediaWiki installation.
  php:
    # ...
    secrets:
      # ...
      # Secrets storing credentials needed to connect to test databases:
      - source: mssqlserver_user
        target: mssqlserver_user
      - source: mssqlserver_password
        target: mssqlserver_password
      - source: postgresql_user
        target: postgresql_user
      - source: postgresql_password
        target: postgresql_password
      - source: mongodb_password
        target: mongodb_password
    networks:
      # ...
      # Networks shared with containers:
      converter:
      mongodb:
      mssqlserver:
      postgresql:
      man:
      apk:
      whois:
      mmdblookup:
      tzdata:
      youtube-dl:
      mathjax:
      maxima:
      octave:
      cadabra:
      yacas:
      hevea:
      asymptote:
      graphviz:
      mscgen:
      plantuml:
      ploticus:
      easytimeline:
      gnuplot:
      lilypond:
      vega:
      mermaid:
      bpmn:
      kroki:
      pdfminer:
      echarts:
      zint:

  # Web frontend:
  nginx:
  # or angie.
    # ...
    volumes:
      # ...
      # Volumes shared with media containers mounted so that they are served
      # by the path defined in $wgExternalDataSources['...']['scripts'];
      - mathjax:/var/www/js/mathjax:ro
      - ploticus:/var/www/js/ploticus:ro
      - gnuplot:/var/www/js/gnuplot:ro
      - asymptote:/var/www/js/asymptote:ro
      - hevea:/var/www/js/hevea:ro
      - vega:/var/www/js/vega:ro
      - vega_uploads:/var/www/js/vega_uploads:ro
      - mermaid:/var/www/js/mermaid:ro
      - echarts:/var/www/js/echarts:ro

  # Containers for testing ExternalData\Presets\Test
  mongodb:
    image: mongo:latest
    restart: unless-stopped
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD_FILE: /run/secrets/root_password
      MONGO_INITDB_USERNAME: wikiuser
      MONGO_INITDB_PASSWORD_FILE: /run/secrets/password
      MONGO_INITDB_DATABASE: test
    volumes:
      - mongodb:/var/lib/mongodb
      - ./mongodb/init.sh:/docker-entrypoint-initdb.d/init.sh:ro
      - ./mongodb/zips:/docker-entrypoint-initdb.d/zips:ro
    secrets:
      - source: mongodb_password
        target: password
      - source: mongodb_root_password
        target: root_password
    tmpfs:
      - /tmp
    # If MediaWiki installation is not dockerised, this container should expose a port,
    # which should be inserted into $wgExternalDataSources['mongodb']['url'] and
    # $wgExternalDataSources['mongodb']['version url']
    networks:
      mongodb:
    cpu_shares: 512
    deploy:
      resources:
        limits:
          memory: 512m

  #
  # Image converter adding a watermark.
  # A dockerised image conversion service.
  # To use, set
  # $wgCustomConvertCommand = '/usr/local/bin/convert.sh %s %w %h %d "' . $wgCookiePrefix . '"';
  # and create /usr/local/bin/convert.sh:
  # #!/bin/sh
  # set -eux
  # SOURCE="$1"
  # WIDTH="$2"
  # HEIGHT="$3"
  # DESTINATION="$4"
  # WATERMARK="$5"
  # TYPE=${SOURCE#*.}
  # curl --data-binary "@$SOURCE" --output "$DESTINATION" \
  #     "http://converter/cgi-bin/cgi.sh?type=$TYPE&width=$WIDTH&height=$HEIGHT&watermark=$WATERMARK"
  #
  converter:
    build:
      context: ./services/cgi
      args:
        APK: graphicsmagick@community
        COMMAND: |
          WM=''
          if [ -n "$$watermark" ]; then
              for EXT in svg webp png gif jpeg jpg; do
                  if [ -f "/usr/local/share/watermarks/$$watermark/watermark.$$EXT" ]; then
                      WM="/usr/local/share/watermarks/$$watermark/watermark.$$EXT"
                      break
                  fi
              done
          fi
          if [ -z "$$WM" ]; then
              /usr/bin/gm convert "$$type:-" -resize "$${width}x$${height}" "$$type:-"
          else
              /usr/bin/gm composite -resize "$${width}x$${height}" -tile "$$WM" "$$type:-" "$$type:-"
          fi
        VERSION_COMMAND: /usr/bin/gm version | head -n5
        CONTENT_TYPE: image/$$type
    restart: unless-stopped
    environment:
      BINARY: 1
    tmpfs:
      - /tmp
    volumes:
      - logos_wikiproject:/usr/local/share/watermarks/wikiproject:ro
    networks:
      converter:
    cpu_shares: 1024
    deploy:
      resources:
        limits:
          memory: 512m

  #
  # Databases
  #

  # MS SQL Server:
  mssqlserver:
    build:
      context: ./mssqlserver
      args:
        VERSION: 2022-latest
    restart: unless-stopped
    volumes:
      - mssqlserver:/var/opt/mssql
    secrets:
      - source: mssqlserver_sa_password
        target: sa_password
      - source: mssqlserver_user
        target: user
      - source: mssqlserver_password
        target: password
    tmpfs:
      - /tmp
      - /var/opt/mssql/tempdb
    # If MediaWiki installation is not dockerised, this container should expose a port,
    # which should be inserted into $wgExternalDataSources['mssqlserver']['url'] and
    # $wgExternalDataSources['mssqlserver']['version url']
    networks:
      mssqlserver:
    cpu_shares: 512
    deploy:
      resources:
        limits:
          memory: 2g

  # PostgreSQL:
  postgresql:
    build:
      context: ./postgresql
      args:
        VERSION: 16.4
    restart: unless-stopped
    volumes:
      - postgresql:/var/lib/postgresql/data
    secrets:
      - source: postgresql_postgres_password
        target: postgres_password
      - source: postgresql_user
        target: user
      - source: postgresql_password
        target: password
    tmpfs:
      - /tmp
    # If MediaWiki installation is not dockerised, this container should expose a port,
    # which should be inserted into $wgExternalDataSources['postgresql']['url'] and
    # $wgExternalDataSources['postgresql']['version url']
    networks:
      postgresql:
    cpu_shares: 512
    deploy:
      resources:
        limits:
          memory: 2g

  # Containers for testing ExternalData\Presets\Reference
  man:
    build:
      context: ./cgi
      args:
        APK: docs mandoc-apropos
        COMMAND: /usr/bin/man -c -T html $$key "$$topic"
        CONTENT_TYPE: text/html
        VERSION_COMMAND: apk info mandoc | head -5 | tail -4
    restart: unless-stopped
    tmpfs:
      - /tmp
    # If MediaWiki installation is not dockerised, this container should expose a port,
    # which should be inserted into $wgExternalDataSources['man']['url'] and
    # $wgExternalDataSources['man']['version url']
    networks:
      man:
    cpu_shares: 256
    deploy:
      resources:
        limits:
          memory: 128m

  apk:
    build:
      context: ./cgi
      args:
        STARTUP: apk update
        COMMAND: apk "$$subcommand" "$$key" "$$package"
    restart: unless-stopped
    tmpfs:
      - /tmp
    # If MediaWiki installation is not dockerised, this container should expose a port,
    # which should be inserted into $wgExternalDataSources['apk']['url'] and
    # $wgExternalDataSources['apk']['version url']
    networks:
      apk:
    cpu_shares: 256
    deploy:
      resources:
        limits:
          memory: 128m

  whois:
    build:
      context: ./cgi
      args:
        APK: whois
        COMMAND: /usr/bin/whois "$$domain"
    restart: unless-stopped
    tmpfs:
      - /tmp
    networks:
      whois:
    # If MediaWiki installation is not dockerised, this container should expose a port,
    # which should be inserted into $wgExternalDataSources['whois']['url'] and
    # $wgExternalDataSources['whois']['version url']
    cpu_shares: 256
    deploy:
      resources:
        limits:
          memory: 64m

  mmdb:
    build:
      context: ./cgi
      args:
        GO: github.com/maxmind/mmdbinspect/cmd/mmdbinspect@latest
        WGET: >-
          https://github.com/P3TERX/GeoLite.mmdb/raw/download/GeoLite2-ASN.mmdb
          https://github.com/P3TERX/GeoLite.mmdb/raw/download/GeoLite2-City.mmdb
          https://github.com/P3TERX/GeoLite.mmdb/raw/download/GeoLite2-Country.mmdb
        COMMAND: '"$$GOPATH/bin/mmdbinspect" -db "/usr/share/downloads/GeoLite2-$$db.mmdb" "$$ip"'
        CONTENT_TYPE: text/json
        VERSION_COMMAND: |
          echo "mmdbinspect. Copyright (c) 2019 - 2025 by MaxMind, Inc,
          licensed under the Apache License, Version 2.0 or the MIT License, at your option"
    restart: unless-stopped
    tmpfs:
      - /tmp
    # If MediaWiki installation is not dockerised, this container should expose a port,
    # which should be inserted into $wgExternalDataSources['mmdb']['url'] and
    # $wgExternalDataSources['mmdb']['version url']
    networks:
      mmdblookup:
    cpu_shares: 1024
    deploy:
      resources:
        limits:
          memory: 128m

  tzdata:
    build:
      context: ./cgi
      args:
        APK: tzdata tzdata-utils
        COMMAND: |
          echo -e 'name,weekday,month,day,time,year,abbr' && \
          ( for DIR in /usr/share/zoneinfo/*; do \
              for FILE in $$DIR/*; do \
                  if [ -f "$$FILE" ]; then \
                      echo "$$( basename "$$DIR" )/$$( basename "$$FILE" )"; \
                  fi; \
              done; \
          done; ) | xargs /usr/sbin/zdump | sed -E 's/\s+/,/g'
        CONTENT_TYPE: text/css
        VERSION_COMMAND: /usr/sbin/zdump --version
    restart: unless-stopped
    tmpfs:
      - /tmp
    # If MediaWiki installation is not dockerised, this container should expose a port,
    # which should be inserted into $wgExternalDataSources['tzdata']['url'] and
    # $wgExternalDataSources['tzdata']['version url']
    networks:
      tzdata:
    cpu_shares: 256
    deploy:
      resources:
        limits:
          memory: 64m

  youtube-dl:
    build:
      context: ./cgi
      args:
        APK: python3
        BINARY: https://github.com/ytdl-org/ytdl-nightly/releases/download/2024.08.07/youtube-dl
        COMMAND: /usr/local/bin/youtube-dl -j "$$url"
        VERSION_COMMAND: /usr/local/bin/youtube-dl --version
        CONTENT_TYPE: text/json
    restart: unless-stopped
    tmpfs:
      - /tmp
    # If MediaWiki installation is not dockerised, this container should expose a port,
    # which should be inserted into $wgExternalDataSources['youtube-dl']['url'] and
    # $wgExternalDataSources['youtube-dl']['version url']
    networks:
      youtube-dl:
    cpu_shares: 1024
    deploy:
      resources:
        limits:
          memory: 256m

  #
  # Containers, connected by testing ExternalData\Presets\Math:
  #
  # This service does not fully replace the MathJax extension (https://github.com/alex-mashin/MathJax).
  mathjax:
    build:
      context: ./cgi
      args:
        APK: coreutils
        NODE: commander mathjax-full
        BINARY: https://raw.githubusercontent.com/alex-mashin/MathJax/master/src/tex2mml.mjs
        WGET: https://raw.githubusercontent.com/alex-mashin/MathJax/master/assets/config.fixed.json
        COMMAND: >-
          /usr/bin/node --stack-size=1024 --stack-trace-limit=1000
          /usr/local/bin/tex2mml.mjs -
        VERSION_COMMAND: /usr/bin/node /usr/local/bin/tex2mml.mjs -V
        CONTENT_TYPE: text/mathml
    environment:
      ERRORS: FATAL
    restart: unless-stopped
    volumes:
      - mathjax:/usr/local/bin/node_modules/mathjax-full/es5
    tmpfs:
      - /tmp
    # If MediaWiki installation is not dockerised, this container should expose a port,
    # which should be inserted into $wgExternalDataSources['mathjax']['url'] and
    # $wgExternalDataSources['mathjax']['version url']
    networks:
      mathjax:
    cpu_shares: 512
    deploy:
      resources:
        limits:
          memory: 256m

  maxima:
    build:
      context: ./cgi
      args:
        SRC: >-
          https://altushost-swe.dl.sourceforge.net/project/maxima/Maxima-source/5.47.0-source/maxima-5.47.0.tar.gz?viasf=1
        APK: sbcl texinfo gnuplot
        COMMAND: >-
          /usr/local/bin/maxima --disable-readline --very-quiet | sed '/^;;;/d' &&
          find /tmp -type f -name '*.gnuplot' -delete &&
          find /tmp -type f -name '*.svg' -delete
        CONTENT_TYPE: text
        VERSION_COMMAND: /usr/local/bin/maxima --version
    restart: unless-stopped
    tmpfs:
      - /tmp
      - /home/www-data/.maxima
    # If MediaWiki installation is not dockerised, this container should expose a port,
    # which should be inserted into $wgExternalDataSources['maxima']['url'] and
    # $wgExternalDataSources['maxima']['version url']
    networks:
      maxima:
    cpu_shares: 4096
    deploy:
      resources:
        limits:
          memory: 1024m

  octave:
    build:
      context: ./cgi
      args:
        APK: sed gnuplot octave@testing
        COMMAND: |-
          TMP=$$(mktemp -u /home/www-data/octaveXXXXXX) && cat > "$$TMP.m" &&
          /usr/bin/octave \
          -p '/home/www-data' \
          --eval "warning('off', 'all'); publish ('$$TMP.m', struct (
              'format', 'html',
              'outputDir', '/home/www-data',
              'imageFormat', 'svg',
              'showCode', $${code:-true}
          ))" &&
          cat "$$TMP.html" |
          sed -E 's%<img src="([^"]+\.svg)"\s+alt="\1">%\n\1\n%g' |
          sed -E 's%^(.+\.svg)$$%cat /home/www-data/\1%eg' |
          sed -E 's/<\?xml\s+version="1.0"\s+encoding="utf-8"\s+standalone="no"\?>//gi' |
          sed -E 's/<h1>.*?<\/h1>//i' &&
          sleep 1 && rm -f /home/www-data/*.svg "$$TMP.m" "$$TMP.html"
        CONTENT_TYPE: text/html
        VERSION_COMMAND: /usr/bin/octave -v --silent | head -3
    environment:
      ERRORS: FATAL
    restart: unless-stopped
    tmpfs:
      - /tmp
      - /home/www-data:uid=1000
    # If MediaWiki installation is not dockerised, this container should expose a port,
    # which should be inserted into $wgExternalDataSources['octave']['url'] and
    # $wgExternalDataSources['octave']['version url']
    networks:
      maxima:
    cpu_shares: 2048
    deploy:
      resources:
        limits:
          memory: 1024m

  cadabra:
    build:
      context: ./cgi
      args:
        APK: >-
          boost1.84-dev python3-dev
          gtkmm3-dev openssl-dev gmp-dev sqlite-dev ossp-uuid-dev readline-dev
          py3-matplotlib py3-mpmath py3-sympy
        GIT: https://github.com/kpeeters/cadabra2
        BUILD_COMMAND: >-
          apk add --no-cache cmake && mkdir -p build && cd build &&
          cmake .. && make && make install && apk del cmake
        COMMAND: |-
          TMP=$$(mktemp -u /home/www-data/cdbXXXXXX) && cat > "$$TMP.cnb" && \
          PARAMS='' && \
          if [ "$$code" = 'false' ]; then \
              PARAMS="$$PARAMS --strip-code"; \
          fi && \
          if [ "$$cells" = 'false' ]; then \
              PARAMS="$$PARAMS --hide-input-cells"; \
          fi && \
          /bin/sh -c "/usr/local/bin/cadabra2html $$PARAMS $$TMP.cnb" && rm -f "$$TMP.cnb"
        CONTENT_TYPE: text/html
        VERSION_COMMAND: /usr/local/bin/cadabra2-cli --version
    restart: unless-stopped
    tmpfs:
      - /tmp
      - /home/www-data:uid=1000
    # If MediaWiki installation is not dockerised, this container should expose a port,
    # which should be inserted into $wgExternalDataSources['cadabra']['url'] and
    # $wgExternalDataSources['cadabra']['version url']
    networks:
      cadabra:
    cpu_shares: 2048
    deploy:
      resources:
        limits:
          memory: 2048m

  yacas:
    build:
      context: ./cgi
      args:
        APK: py3-sphinx_rtd_theme gnuplot
        PIP: sphinxcontrib-bibtex
        # GIT: https://github.com/grzegorzmazur/yacas.git
        GIT: https://github.com/alex-mashin/yacas.git
        CMAKE_ARGS: -DCMAKE_BUILD_TYPE=Release -DENABLE_CYACAS_GUI=false -DENABLE_DOCS=true
        COMMAND: /usr/local/bin/yacas -p -c /dev/stdin && rm -f /tmp/*.svg /tmp/yacas-*
        CONTENT_TYPE: text/html
        VERSION_COMMAND: /usr/local/bin/yacas -v
    environment:
      ERRORS: FATAL
    restart: unless-stopped
    tmpfs:
      - /tmp
    # If MediaWiki installation is not dockerised, this container should expose a port,
    # which should be inserted into $wgExternalDataSources['yacas']['url'] and
    # $wgExternalDataSources['yacas']['version url']
    networks:
      yacas:
    cpu_shares: 2048
    deploy:
      resources:
        limits:
          memory: 1024m

  hevea:
    build:
      context: ./cgi
      args:
        ALPINE: edge
        APK: >-
          gmp-dev mpfr-dev pari-dev libxext-dev libpng-dev jpeg-dev readline-dev
          libx11-dev libxt-dev libxft-dev libao-dev fltk-dev mesa-dev gsl-dev ruby
          libntl-dev@community micropython@community
          libmpfi-dev@testing virtualgl-dev@testing
        SRC: >-
          https://www-fourier.univ-grenoble-alpes.fr/~parisse/giac/heveatomml.tgz
          https://www-fourier.univ-grenoble-alpes.fr/~parisse/giac/giac_stable.tgz
        CXXFLAGS: -std=c++17 -march=native -O2 -I/src/version2
        GIT: |-
          https://github.com/vectorclass/version2
          https://github.com/YannickChevalier/hevea-mathjax
        WGET: >-
          https://raw.githubusercontent.com/alex-mashin/giac-hevea/refs/heads/master/giac.tex
          https://raw.githubusercontent.com/alex-mashin/giac-hevea/refs/heads/master/giac.js
          https://raw.githubusercontent.com/alex-mashin/giac-hevea/refs/heads/master/giacwasm.js
        PRE_BUILD_COMMAND: |-
          if [ -f src/mkjs.cc ]; then
              g++ src/mkjs.cc -o src/mkjs;
          fi
        STARTUP: |-
          GIAC_DIR='/usr/share/downloads' && \
          mkdir -p /www/js && \
          cp "$$GIAC_DIR/giac.js" /www/js/ && cp "$$GIAC_DIR/giacwasm.js" /www/js/ && \
          if [ -n "$$MATHJAX" ]; then \
              ORIGINAL='https://cdnjs\.cloudflare\.com/ajax/libs/mathjax/[0-9]+\.[0-9]+.[0-9]+/MathJax\.js\?config=TeX-AMS-MML_HTMLorMML' && \
              sed -i "s!$$ORIGINAL!$$MATHJAX!g" "$$GIAC_DIR/giac.tex"; \
          fi && \
          OLD_HERE='\\loadgiacscript\[""\]' && NEW_HERE='\\loadgiacscript["/js/giac/"]' && \
          sed -iE "s~$$OLD_HERE~$$NEW_HERE~g" "$$GIAC_DIR/giac.tex"
        COMMAND: |-
          TMP=$$( mktemp -u '/home/www-data/texXXXXXX' ) && \
          cat > "$$TMP.tex" && \
          if [ "$$giac" == 'false' ]; then \
              /usr/local/bin/hevea -mathjax -o "$$TMP.html" "$$TMP.tex"; \
          else \
              GIAC_DIR='/usr/local/share/giac/doc' && \
              /usr/local/bin/hevea -I "$$GIAC_DIR" -mathjax -o "$$TMP.html" "$$TMP.tex"; \
          fi && \
          /usr/local/bin/hevea2mml "$$TMP" && cat "$$TMP.html" && rm -f "$$TMP.*"
        CONTENT_TYPE: text/html
        VERSION_COMMAND: /usr/local/bin/hevea -version | head -1 && /usr/local/bin/giac -v 2>/dev/null | tail -n2
    environment:
      ERRORS: FATAL
    restart: unless-stopped
    volumes:
      - hevea:/www/js
    tmpfs:
      - /tmp
      - /home/www-data:uid=1000
    # If MediaWiki installation is not dockerised, this container should expose a port,
    # which should be inserted into $wgExternalDataSources['hevea']['url'] and
    # $wgExternalDataSources['hevea']['version url']
    networks:
      hevea:
    cpu_shares: 2048
    deploy:
      resources:
        limits:
          memory: 1024m

  gnuplot:
    build:
      context: ./cgi
      args:
        APK: gnuplot
        COMMAND: >-
          /usr/bin/gnuplot -e "
              set terminal svg size $$width,$$height dynamic
              mouse jsdir '/js/gnuplot' name '$$name'
              font 'arial,$$size' enhanced $$heads;
          " - | sed "1d; s/^<svg/<svg width=\"$$width\" height=\"$$height\"/"
        VERSION_COMMAND: /usr/bin/gnuplot -V
        CONTENT_TYPE: text/svg
    restart: unless-stopped
    tmpfs:
      - /tmp
    volumes:
      - gnuplot:/usr/share/gnuplot/6.0/js
    # If MediaWiki installation is not dockerised, this container should expose a port,
    # which should be inserted into $wgExternalDataSources['gnuplot']['url'] and
    # $wgExternalDataSources['gnuplot']['version url']
    networks:
      gnuplot:
    cpu_shares: 1024
    deploy:
      resources:
        limits:
          memory: 512m

  asymptote:
    build:
      context: ./cgi
      args:
        ALPINE: edge
        APK: patch asymptote@community asymptote-doc@community imagemagick@community
        STARTUP: |
          patch -d /etc/ImageMagick-7 <<'PATCH'
          --- delegates.xml.orig	2026-04-16 18:30:31.122123097 -0400
          +++ delegates.xml	2026-04-16 18:29:38.661850697 -0400
          @@ -103,7 +103,7 @@
             <delegate decode="ppt" command="&apos;libreoffice&apos; --convert-to pdf --outdir `dirname &apos;%i&apos;` &apos;%i&apos; 2&gt; &apos;%u&apos;; /bin/mv &apos;%i.pdf&apos; &apos;%o&apos;"/>
             <delegate decode="pptx" command="&apos;libreoffice&apos; --convert-to pdf --outdir `dirname &apos;%i&apos;` &apos;%i&apos; 2&gt; &apos;%u&apos;; /bin/mv &apos;%i.pdf&apos; &apos;%o&apos;"/>
             <delegate decode="ps" encode="prt" command="&apos;lpr&apos; &apos;%i&apos;"/>
          -  <delegate decode="ps:alpha" stealth="True" command="&apos;gs&apos; -sstdout=%%stderr -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 &apos;-sDEVICE=png16malpha&apos; -dTextAlphaBits=%u -dGraphicsAlphaBits=%u &apos;-r%s&apos; %s &apos;-sOutputFile=%s&apos; &apos;-f%s&apos; &apos;-f%s&apos;"/>
          +  <delegate decode="ps:alpha" stealth="True" command="&apos;gs&apos; -sstdout=%%stderr -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 -dALLOWPSTRANSPARENCY &apos;-sDEVICE=png16malpha&apos; -dTextAlphaBits=%u -dGraphicsAlphaBits=%u &apos;-r%s&apos; %s &apos;-sOutputFile=%s&apos; &apos;-f%s&apos; &apos;-f%s&apos;"/>
             <delegate decode="ps:cmyk" stealth="True" command="&apos;gs&apos; -sstdout=%%stderr -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 &apos;-sDEVICE=pamcmyk32&apos; -dTextAlphaBits=%u -dGraphicsAlphaBits=%u &apos;-r%s&apos; %s &apos;-sOutputFile=%s&apos; &apos;-f%s&apos; &apos;-f%s&apos;"/>
             <delegate decode="ps:color" stealth="True" command="&apos;gs&apos; -sstdout=%%stderr -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 &apos;-sDEVICE=png16m&apos; -dTextAlphaBits=%u -dGraphicsAlphaBits=%u &apos;-r%s&apos; %s &apos;-sOutputFile=%s&apos; &apos;-f%s&apos; &apos;-f%s&apos;"/>
             <delegate decode="ps" encode="eps" mode="bi" command="&apos;gs&apos; -sstdout=%%stderr -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 &apos;-sDEVICE=eps2write&apos; &apos;-sOutputFile=%o&apos; &apos;-f%i&apos;"/>
          PATCH
        COMMAND: >-
          TMP=$$( mktemp -u '/home/www-data/asyXXXXXX' ) &&
          /usr/bin/asy -q -inlineimage -webgl2 -f "$$output" -o "$$TMP" - &&
          cat "$$TMP.svg" "$$TMP.html" 2>/dev/null &&
          rm -f "$$TMP.svg" "$$TMP.html"
        VERSION_COMMAND: /usr/bin/asy -version | head -1
        CONTENT_TYPE: text/svg
    restart: unless-stopped
    tmpfs:
      - /tmp
      - /home/www-data:uid=1000
    volumes:
      - asymptote:/usr/share/asymptote/webgl
    # If MediaWiki installation is not dockerised, this container should expose a port,
    # which should be inserted into $wgExternalDataSources['asymptote']['url'] and
    # $wgExternalDataSources['asymptote']['version url']
    networks:
      asymptote:
    cpu_shares: 1024
    deploy:
      resources:
        limits:
          memory: 1024m
  #
  # Multimedia containers, connected by testing ExternalData\Presets\Media:
  #
  lilypond:
    build:
      context: ./cgi
      args:
        APK: lilypond
        COMMAND: >-
          TMP=$$(mktemp -u) && lilypond -s -dbackend=svg -dcrop -o $$TMP - && sleep 1 && cat "$$TMP.cropped.svg" &&
          rm -f "$$TMP" && rm -f "$$TMP.*"
        CONTENT_TYPE: text/svg
        VERSION_COMMAND: lilypond -v
    restart: unless-stopped
    environment:
      ERRORS: FATAL
    tmpfs:
      - /tmp
    # If MediaWiki installation is not dockerised, this container should expose a port,
    # which should be inserted into $wgExternalDataSources['lilypond']['url'] and
    # $wgExternalDataSources['lilypond']['version url']
    networks:
      lilypond:
    cpu_shares: 1024
    deploy:
      resources:
        limits:
          memory: 512m

  graphviz:
    build:
      context: ./cgi
      args:
        APK: graphviz fontconfig ttf-freefont
        COMMAND: /usr/bin/dot -K"$$layout" -Tsvg
        CONTENT_TYPE: text/svg
    restart: unless-stopped
    # This is only needed for graphs with images.
    volumes:
      # If this is a wiki farm:
      # mount $wgUploadDirectory of each wiki in the farm as follows. It is assumed that $wgDbName = 'wiki'.
      # Also, set $wgExternalDataSources['graphviz']['mounted farm uploads'] = '/var/images'.
      - uploads_wiki:/var/images/wiki:ro
      # If there is only one wiki:
      # mount its $wgUploadDirectory as follows. It is assumed that $wgUploadDirectory = '/var/www/wiki/w/images'.
      - uploads_wiki:/var/www/wiki/w/images:ro
    tmpfs:
      - /tmp
    # If MediaWiki installation is not dockerised, this container should expose a port,
    # which should be inserted into $wgExternalDataSources['graphviz']['url'] and
    # $wgExternalDataSources['graphviz']['version url']
    networks:
      graphviz:
    cpu_shares: 1024
    deploy:
      resources:
        limits:
          memory: 512m

  mscgen:
    build:
      context: ./cgi
      args:
        APK: gd-dev
        SRC: https://www.mcternan.me.uk/mscgen/software/mscgen-src-0.20.tar.gz
        COMMAND: /usr/local/bin/mscgen -Tsvg -o -
        CONTENT_TYPE: text/svg
        VERSION_COMMAND: /usr/local/bin/mscgen -l | head -2
    restart: unless-stopped
    tmpfs:
      - /tmp
    # If MediaWiki installation is not dockerised, this container should expose a port,
    # which should be inserted into $wgExternalDataSources['mscgen']['url'] and
    # $wgExternalDataSources['mscgen']['version url']
    networks:
      mscgen:
    cpu_shares: 1024
    deploy:
      resources:
        limits:
          memory: 512m

  plantuml:
    build:
      context: ./cgi
      args:
        APK: graphviz fontconfig ttf-freefont
        JAR: >-
          https://downloads.sourceforge.net/project/plantuml/plantuml.jar
          http://beta.plantuml.net/plantuml-jlatexmath.zip
        COMMAND: java -jar /usr/share/java/plantuml.jar -tsvg -charset UTF-8 -p -theme "$$theme"
        CONTENT_TYPE: text/svg
        VERSION_COMMAND: java -jar /usr/share/java/plantuml.jar -version | sed '/^\s*$$/d' | head -n-1
    environment:
      LOG4J_FORMAT_MSG_NO_LOOKUPS: true
    restart: unless-stopped
    tmpfs:
      - /tmp
    # If MediaWiki installation is not dockerised, this container should expose a port,
    # which should be inserted into $wgExternalDataSources['plantuml']['url'] and
    # $wgExternalDataSources['plantuml']['version url']
    networks:
      plantuml:
    cpu_shares: 1024
    deploy:
      resources:
        limits:
          memory: 1g

  ploticus:
    build:
      context: ./cgi
      args:
        APK: freetype-dev fontconfig ttf-freefont zlib-dev
        GIT: https://salsa.debian.org/debian/ploticus
        BRANCH: debian/latest
        BUILD_COMMAND: >-
          cd src &&
          sed -i 's/CC = gcc/CC = gcc -std=gnu17/' Makefile &&
          make -j$$(( $$( nproc --all ) * 2 )) && make install
        WGET: https://github.com/alex-mashin/svg_mouseover_js/archive/refs/heads/master.zip
        COMMAND: >-
          /usr/local/bin/pl -stdin -outlabel "$$title" -tightcrop -textsize "$$fontsize" -font 'FreeSans'
          -svg -omit_xml_declaration -xml_encoding utf-8 -map -noshell -o stdout
        # -csmap
        CONTENT_TYPE: text/svg
        VERSION_COMMAND: /usr/local/bin/pl -? 2>&1 | head -2
    environment:
      TDH_ERRMODE: cgi
      ERRORS: FATAL
    restart: unless-stopped
    tmpfs:
      - /tmp
    volumes:
      - ploticus:/usr/share/downloads
    # If MediaWiki installation is not dockerised, this container should expose a port,
    # which should be inserted into $wgExternalDataSources['ploticus']['url'] and
    # $wgExternalDataSources['ploticus']['version url']
    networks:
      ploticus:
    cpu_shares: 1024
    deploy:
      resources:
        limits:
          memory: 512m

  easytimeline:
    build:
      context: ./cgi
      args:
        APK: fontconfig ttf-freefont perl
        GIT: https://salsa.debian.org/debian/ploticus
        BRANCH: debian/latest
        BUILD_COMMAND: >-
          cd src &&
          sed -i 's/CC = gcc/CC = gcc -std=gnu17/' Makefile &&
          make -j$$(( $$( nproc --all ) * 2 )) && make install
        WGET: >-
          https://github.com/alex-mashin/svg_mouseover_js/archive/refs/heads/master.zip
          https://raw.githubusercontent.com/wikimedia/mediawiki-extensions-timeline/master/scripts/EasyTimeline.pl
        COMMAND: >-
          TMP=$$(mktemp -u) && cat > "$$TMP.tl" &&
          perl /usr/share/downloads/EasyTimeline.pl -i "$$TMP.tl" -m -P "/usr/local/bin/pl" -T /tmp -A "$$path"
          -f '/usr/share/fonts/freefont/FreeSans.otf' -s -b
          1>/dev/null && (
          if [ ! -f "$$TMP.err" ]; then
              cat "$$TMP.svg";
          else
              cat "$$TMP.err" >&2;
          fi ) &&
          rm -f "$$TMP.tl" "$$TMP.png" "$$TMP.svg" "$$TMP.err"
        CONTENT_TYPE: text/svg
        VERSION_COMMAND: perl /usr/share/downloads/EasyTimeline.pl | head -3
    environment:
      TDH_ERRMODE: cgi
      ERRORS: FATAL
    restart: unless-stopped
    tmpfs:
      - /tmp
    # If MediaWiki installation is not dockerised, this container should expose a port,
    # which should be inserted into $wgExternalDataSources['easytimeline']['url'] and
    # $wgExternalDataSources['easytimeline']['version url']
    networks:
      easytimeline:
    cpu_shares: 1024
    deploy:
      resources:
        limits:
          memory: 512m

  vega:
    build:
      context: ./cgi
      args:
        APK: python3 pkgconfig make g++ pixman-dev cairo-dev pango-dev
        NODE: vega-cli vega-projection-extended vega-lite vega-embed
        COMMAND: /usr/local/bin/node_modules/vega-cli/bin/vg2svg -b /var/lib/uploads
        CONTENT_TYPE: text/svg
    environment:
      ERRORS: FATAL
    restart: unless-stopped
    volumes:
      - vega:/usr/local/bin/node_modules
      - vega_uploads:/var/lib/uploads
    tmpfs:
      - /tmp
    # If MediaWiki installation is not dockerised, this container should expose a port,
    # which should be inserted into $wgExternalDataSources['vega']['url'] and
    # $wgExternalDataSources['vega']['version url']
    networks:
      vega:
    cpu_shares: 1024
    deploy:
      resources:
        limits:
          memory: 512m

  mermaid:
    build:
      context: ./cgi
      args:
        APK: chromium
        NODE: "puppeteer @mermaid-js/mermaid-cli"
        COMMAND: >-
          /usr/local/bin/node_modules/.bin/mmdc -p /usr/local/bin/.puppeteerrc.json -q -I "$${id}_svg"
          -s "$$scale" -w "$$width" -H "$$height"
          -t "$$theme" -b "$$background" -i - -e svg -o -
        CONTENT_TYPE: text/svg
        VERSION_COMMAND: /usr/local/bin/node_modules/.bin/mmdc -V
    environment:
      PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: true
      PUPPETEER_EXECUTABLE_PATH: /usr/bin/chromium-browser
      ERRORS: FATAL # Shutting up [@zenuml/core] Store is a function and is not initiated in 1 second.
    security_opt:
      - seccomp:./cgi/chrome.json
    restart: unless-stopped
    volumes:
      - mermaid:/usr/local/bin/node_modules/mermaid/dist
    tmpfs:
      - /tmp
    # If MediaWiki installation is not dockerised, this container should expose a port,
    # which should be inserted into $wgExternalDataSources['mermaid']['url'] and
    # $wgExternalDataSources['mermaid']['version url']
    networks:
      mermaid:
    cpu_shares: 1024
    deploy:
      resources:
        limits:
          memory: 1024m

  echarts:
    build:
      context: ./cgi
      args:
        APK: chromium
        NODE: "puppeteer echarts"
        SCRIPT: |
          const echarts = require( 'echarts' );
          process.stdin.on('data', ( data ) => {
              // Parameters:
              let theme = null;
              let width = 400;
              let height = 300;
              let locale = 'EN';
              // echarts.registerLocale (locale, localeCfg);
              process.argv.forEach( ( val, index, array ) => {
                  let matches = val.match( /(theme|width|height|locale)\s*=\s*"(.+)"/ );
                  if ( matches ) {
                      if ( matches[1] === 'theme' ) {
                          theme = matches[2];
                      } else if ( matches[1] === 'width' ) {
                          width = parseInt( matches[2] );
                      } else if ( matches[1] === 'height' ) {
                          height = parseInt( matches[2] );
                      } else if ( matches[1] === 'locale' ) {
                          locale = matches[2];
                      }
                  }
              });
              // In SSR mode the first container parameter is not required:
              let chart = echarts.init( null, theme, {
                  renderer: 'svg', // must use SVG rendering mode
                  ssr: true, // enable SSR
                  width: width,
                  height: height,
                  locale: locale
              });
              // Use setOption as normal
              chart.setOption( JSON.parse( data ) );
              // Output a string
              console.log( chart.renderToSVGString() );
              // If chart is no longer useful, consider dispose it to release memory.
              chart.dispose();
              chart = null;
          });
        COMMAND: node /usr/local/bin/script width="$$width" height="$$height" theme="$$theme" locale="$$locale"
        CONTENT_TYPE: text/svg
        VERSION_COMMAND: echo 'console.log( require( "/usr/local/bin/node_modules/echarts" ).version );' | node
    environment:
      PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: true
      PUPPETEER_EXECUTABLE_PATH: /usr/bin/chromium-browser
    security_opt:
      - seccomp:./cgi/chrome.json
    restart: unless-stopped
    volumes:
      - echarts:/usr/local/bin/node_modules/echarts
    tmpfs:
      - /tmp
    # If MediaWiki installation is not dockerised, this container should expose a port,
    # which should be inserted into $wgExternalDataSources['echarts']['url'] and
    # $wgExternalDataSources['echarts']['version url']
    networks:
      echarts:
    cpu_shares: 1024
    deploy:
      resources:
        limits:
          memory: 512m

  bpmn:
    image: pierreschwang/bpmn2svg:development
    restart: unless-stopped
    tmpfs:
      - /tmp
    # If MediaWiki installation is not dockerised, this container should expose a port,
    # which should be inserted into $wgExternalDataSources['bpmn']['url'] and
    # $wgExternalDataSources['bpmn']['version url']
    networks:
      bpmn:
    cpu_shares: 1024
    deploy:
      resources:
        limits:
          memory: 512m

  kroki:
    image: yuzutech/kroki
    depends_on:
      - kroki_mermaid
      - kroki_bpmn
      - kroki_excalidraw
    environment:
      - KROKI_MERMAID_HOST=kroki_mermaid
      - KROKI_BPMN_HOST=kroki_bpmn
      - KROKI_EXCALIDRAW_HOST=kroki_excalidraw
    restart: unless-stopped
    tmpfs:
      - /tmp:exec
    networks:
      kroki:
    cpu_shares: 1024
    deploy:
      resources:
        limits:
          memory: 1g

  kroki_mermaid:
    image: yuzutech/kroki-mermaid
    restart: unless-stopped
    tmpfs:
      - /tmp:exec
    networks:
      kroki:
    cpu_shares: 1024
    deploy:
      resources:
        limits:
          memory: 512m

  kroki_bpmn:
    image: yuzutech/kroki-bpmn
    restart: unless-stopped
    tmpfs:
      - /tmp:exec
    networks:
      kroki:
    cpu_shares: 1024
    deploy:
      resources:
        limits:
          memory: 512m

  kroki_excalidraw:
    image: yuzutech/kroki-excalidraw
    restart: unless-stopped
    tmpfs:
      - /tmp:exec
    networks:
      kroki:
    cpu_shares: 1024
    deploy:
      resources:
        limits:
          memory: 512m

  pdfminer:
    build:
      context: ./cgi
      args:
        APK: py3-pdfminer
        COMMAND: /usr/bin/pdf2txt.py "$$path"
    restart: unless-stopped
    volumes:
      # Mount wiki uploads directory in to the container so that the path looks the same as in MW installation:
      - uploads:/var/www/wiki/files
    tmpfs:
      - /tmp
    # If MediaWiki installation is not dockerised, this container should expose a port,
    # which should be inserted into $wgExternalDataSources['pdfminer']['url'] and
    # $wgExternalDataSources['pdfminer']['version url']
    networks:
      pdfminer:
    cpu_shares: 1024
    deploy:
      resources:
        limits:
          memory: 512m

  zint:
    build:
      context: ./cgi
      args:
        APK: 'zint@community
        COMMAND: >-
          /usr/bin/zint -b "BARCODE_$$type" -d "$$data" $$eci
          --fg="$$fg" --bg="$$bg" --rotate="$$rotate" --scale="$$scale" --direct --filetype=SVG
        CONTENT_TYPE: text/svg
    restart: unless-stopped
    tmpfs:
      - /tmp
    # If MediaWiki installation is not dockerised, this container should expose a port,
    # which should be inserted into $wgExternalDataSources['zint']['url'] and
    # $wgExternalDataSources['zint']['version url']
    networks:
      zint:
    cpu_shares: 512
    deploy:
      resources:
        limits:
          memory: 128m


volumes:
  # Path to a folder that may contain watermark.* for a wiki project.
  logos_wikiproject:
  # Databases:
  mongodb:
  mssqlserver:
  postgresql:
  # Make upload directory (-ies) available to graphviz container. One per wiki in the farm.
  # This volume has to be accessible to MediaWiki at the path $wgUploadDirectory.
  upload_wiki:
  # ... probably, a bind mount.
  # For interactive media JS:
  mathjax:
  ploticus:
  gnuplot:
  asymptote:
  hevea:
  vega_uploads:
  vega:
  mermaid:
  echarts:

# Secrets should be mounted into data sources containers and MediaWiki one,
# or otherwise be made available to MediaWiki, if it is not dockerised.
# They are made known to MediaWiki by $wgExternalDataSources['...']['user file']
# and $wgExternalDataSources['...']['password file'] settings.
secrets:
  # MongoDB:
  mongodb_password:
    file: ./secrets/mongodb/password
  mongodb_root_password:
    file: ./secrets/mongodb/root_password
  # Microsoft SQL Server:
  mssqlserver_sa_password:
    file: ./secrets/mssqlserver/sa_password
  mssqlserver_user:
    file: ./secrets/mssqlserver/user
  mssqlserver_password:
    file: ./secrets/mssqlserver/password
  # PostgreSQL:
  postgresql_postgres_password:
    file: ./secrets/postgresql/postgres_password
  postgresql_user:
    file: ./secrets/postgresql/user
  postgresql_password:
    file: ./secrets/postgresql/password

# If MediaWiki is dockerised, a network per each pair (MW, external data service).
# Otherwise, expose a port for each service container and insert it into
# $wgExternalDataSource['...']['url'] and $wgExternalDataSource['...']['version url'].
networks:
  # ...
  converter:
  mongodb:
  mssqlserver:
  postgresql:
  man:
  apk:
  pip:
  whois:
  mmdblookup:
  tzdata:
  youtube-dl:
  mathjax:
  maxima:
  octave:
  cadabra:
  yacas:
  hevea:
  gnuplot:
  asymptote:
  graphviz:
  mscgen:
  plantuml:
  ploticus:
  easytimeline:
  lilypond:
  vega:
  mermaid:
  kroki:
  bpmn:
  pdfminer:
  echarts:
  zint:
