From 894b20dd749d7d22e6065060ccefb580084daf2f Mon Sep 17 00:00:00 2001 From: turegjorup Date: Fri, 5 Sep 2025 11:21:13 +0200 Subject: [PATCH 01/59] 5402: Remove unmaintained infrastructure code for os2display images --- infrastructure/os2display/Readme.md | 3 - .../os2display/display-api-service/Dockerfile | 155 ------------------ .../display-api-service/docker-entrypoint.sh | 22 --- .../etc/confd/conf.d/env.local.toml | 8 - .../etc/confd/templates/env.local.tmpl | 71 -------- .../os2display/display-api-service/gitconfig | 9 - .../display-api-service/php/apcu.ini | 7 - .../display-api-service/php/fpm.ini | 17 -- .../display-api-service/php/opcache.ini | 15 -- .../display-api-service/php/php.ini | 13 -- infrastructure/os2display/nginx/Dockerfile | 37 ----- .../os2display/nginx/docker-entrypoint.sh | 49 ------ .../nginx/etc/confd/conf.d/default.conf.toml | 7 - .../nginx/etc/confd/conf.d/nginx.conf.toml | 7 - .../etc/confd/templates/default.conf.tmpl | 52 ------ .../nginx/etc/confd/templates/nginx.conf.tmpl | 51 ------ infrastructure/os2display/run.sh | 13 -- 17 files changed, 536 deletions(-) delete mode 100644 infrastructure/os2display/Readme.md delete mode 100644 infrastructure/os2display/display-api-service/Dockerfile delete mode 100644 infrastructure/os2display/display-api-service/docker-entrypoint.sh delete mode 100644 infrastructure/os2display/display-api-service/etc/confd/conf.d/env.local.toml delete mode 100644 infrastructure/os2display/display-api-service/etc/confd/templates/env.local.tmpl delete mode 100644 infrastructure/os2display/display-api-service/gitconfig delete mode 100644 infrastructure/os2display/display-api-service/php/apcu.ini delete mode 100644 infrastructure/os2display/display-api-service/php/fpm.ini delete mode 100644 infrastructure/os2display/display-api-service/php/opcache.ini delete mode 100644 infrastructure/os2display/display-api-service/php/php.ini delete mode 100644 infrastructure/os2display/nginx/Dockerfile delete mode 100644 infrastructure/os2display/nginx/docker-entrypoint.sh delete mode 100644 infrastructure/os2display/nginx/etc/confd/conf.d/default.conf.toml delete mode 100644 infrastructure/os2display/nginx/etc/confd/conf.d/nginx.conf.toml delete mode 100644 infrastructure/os2display/nginx/etc/confd/templates/default.conf.tmpl delete mode 100644 infrastructure/os2display/nginx/etc/confd/templates/nginx.conf.tmpl delete mode 100755 infrastructure/os2display/run.sh diff --git a/infrastructure/os2display/Readme.md b/infrastructure/os2display/Readme.md deleted file mode 100644 index def6e8a45..000000000 --- a/infrastructure/os2display/Readme.md +++ /dev/null @@ -1,3 +0,0 @@ -# OS2display image build - -This folder contains the infrastructure files for building the `os2display/*` images diff --git a/infrastructure/os2display/display-api-service/Dockerfile b/infrastructure/os2display/display-api-service/Dockerfile deleted file mode 100644 index 2809349f8..000000000 --- a/infrastructure/os2display/display-api-service/Dockerfile +++ /dev/null @@ -1,155 +0,0 @@ -FROM php:8.3-fpm-alpine -LABEL maintainer="ITK Dev " - -############# SETUP CONTAINER ############# - -USER root - -ARG APP_VERSION="develop" - -ENV APP_PATH=/var/www/html \ - # PHP - TZ="Europe/Copenhagen" \ - PHP_TIMEZONE="Europe/Copenhagen" \ - PHP_MAX_EXECUTION_TIME="30" \ - PHP_MEMORY_LIMIT="128M" \ - PHP_POST_MAX_SIZE="8M" \ - PHP_UPLOAD_MAX_FILESIZE="2M" \ - PHP_USER="www-data" \ - PHP_GROUP="www-data" \ - PHP_SENDMAIL_PATH='/usr/sbin/sendmail -S host.docker.internal -t -i' \ - # OpCache - PHP_OPCACHE_ENABLED="1" \ - PHP_OPCACHE_JIT="off" \ - PHP_OPCACHE_REVALIDATE_FREQ=0 \ - PHP_OPCACHE_VALIDATE_TIMESTAMPS="1" \ - PHP_OPCACHE_MAX_ACCELERATED_FILES="20000" \ - PHP_OPCACHE_MEMORY_CONSUMPTION="64" \ - PHP_OPCACHE_MAX_WASTED_PERCENTAGE="10" \ - # APCU - PHP_APCU_ENABLED=0 \ - PHP_APCU_ENABLED_CLI=0 \ - PHP_APCU_MEMORY_SIZE="16M" \ - PHP_APCU_SEGMENTS=1 \ - PHP_APCU_PRELOAD_PATH='' \ - # FPM pool - PHP_PM_TYPE="static" \ - PHP_PM_MAX_CHILDREN="8" \ - PHP_PM_MAX_REQUESTS="0" \ - PHP_PM_START_SERVERS="5" \ - PHP_PM_MIN_SPARE_SERVERS="5" \ - PHP_PM_MAX_SPARE_SERVERS="8" \ - # Other - PHP_REQUEST_SLOWLOG_TIMEOUT="0" \ - PHP_SLOWLOG="/dev/stderr" \ - COMPOSER_ALLOW_SUPERUSER=1 - -RUN apk upgrade --no-cache --ignore curl -RUN apk --update add --no-cache \ - libxslt-dev \ - libzip-dev \ - libpng-dev \ - gettext-dev \ - git \ - unzip \ - icu-dev \ - icu-data-full \ - openldap-dev \ - libmcrypt-dev \ - mysql-client \ - libmemcached-libs \ - zlib \ - patch \ - tzdata \ - freetype-dev \ - libjpeg-turbo-dev \ - libjpeg-turbo \ - libwebp-dev \ - && docker-php-ext-configure gd --with-freetype --with-webp --with-jpeg \ - && docker-php-ext-install -j$(nproc) \ - bcmath \ - calendar \ - gd \ - gettext \ - intl \ - ldap \ - mysqli \ - opcache \ - pdo_mysql \ - sysvsem \ - soap \ - xsl \ - zip - -# Extension that are not available via ext- -RUN apk --update add --no-cache --virtual .build-deps autoconf g++ make zlib-dev libmemcached-dev cyrus-sasl-dev \ - && pecl channel-update pecl.php.net \ - && pecl install redis memcached apcu \ - && docker-php-ext-enable apcu redis memcached \ - && apk del .build-deps - -# Install AMQP support -RUN apk --update add --no-cache rabbitmq-c -RUN apk --update add --no-cache --virtual .build-deps autoconf g++ make rabbitmq-c-dev \ - && pecl install amqp \ - && docker-php-ext-enable amqp memcached \ - && apk del .build-deps - -# Install composer -COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer - -# Use default PHP production configuration. -RUN mv ${PHP_INI_DIR}/php.ini-production ${PHP_INI_DIR}/php.ini - -# # Copy custom PHP configuration. -COPY php/opcache.ini ${PHP_INI_DIR}/conf.d/docker-php-ext-opcache.ini -COPY php/php.ini ${PHP_INI_DIR}/conf.d/zz-php.ini -COPY php/apcu.ini ${PHP_INI_DIR}/conf.d/docker-php-ext-apcu.ini - -# Custom FPM configuration. -COPY php/fpm.ini ${PHP_INI_DIR}/../php-fpm.d/zz-fpm-docker.conf - -# Add mhsendmail for mailhog -ADD https://github.com/mailhog/mhsendmail/releases/download/v0.2.0/mhsendmail_linux_amd64 /usr/local/bin/mhsendmail -RUN chmod +x /usr/local/bin/mhsendmail - -# Added FPM health check script (https://github.com/renatomefi/php-fpm-healthcheck) -ADD https://raw.githubusercontent.com/renatomefi/php-fpm-healthcheck/master/php-fpm-healthcheck /usr/local/bin/php-fpm-healthcheck -RUN chmod +x /usr/local/bin/php-fpm-healthcheck - -# Add git global config -COPY gitconfig /root/.gitconfig - -############# SETUP APPLICATION ############# - -# Move site into the container. -ADD https://github.com/os2display/display-api-service/archive/${APP_VERSION}.tar.gz /tmp/app.tar -RUN tar -zxf /tmp/app.tar --strip-components=1 -C ${APP_PATH} \ - && rm /tmp/app.tar - -## Install assets, which requires a HACK as redis is not available (should be removed later on). -RUN APP_ENV=prod composer install --no-dev -o --classmap-authoritative \ - && rm -rf infrastructure \ - && APP_ENV=prod composer clear-cache - -# Install the application. -RUN mkdir -p ${APP_PATH}/config/secrets \ - && chown -R www-data:www-data ${APP_PATH} - -# Copy configuration. -COPY etc /etc/ - -# Install configuration template handler -ADD https://github.com/kelseyhightower/confd/releases/download/v0.16.0/confd-0.16.0-linux-amd64 /usr/local/bin/confd -RUN chmod +x /usr/local/bin/confd - -# Copy confd onfiguration. -COPY etc /etc/ - -COPY docker-entrypoint.sh /usr/local/bin/ -RUN chmod +x /usr/local/bin/docker-entrypoint.sh - -WORKDIR ${APP_PATH} - -CMD ["php-fpm"] -ENTRYPOINT [ "docker-entrypoint.sh" ] diff --git a/infrastructure/os2display/display-api-service/docker-entrypoint.sh b/infrastructure/os2display/display-api-service/docker-entrypoint.sh deleted file mode 100644 index 8ad622b4d..000000000 --- a/infrastructure/os2display/display-api-service/docker-entrypoint.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/sh - -set -eux - -## Run templates with configuration. -/usr/local/bin/confd --onetime --backend env --confdir /etc/confd - -## Bump env.local into PHP for better performance. -composer dump-env prod - -## Warm-up Symfony cache (with the current configuration). -/var/www/html/bin/console --env=prod cache:warmup - -# first arg is `-f` or `--some-option` -if [ "${1#-}" != "$1" ]; then - set -- php-fpm "$@" -fi - -## Start the PHP FPM process. -echo "Starting PHP 8.3 FPM" - -exec "$@" diff --git a/infrastructure/os2display/display-api-service/etc/confd/conf.d/env.local.toml b/infrastructure/os2display/display-api-service/etc/confd/conf.d/env.local.toml deleted file mode 100644 index e7f303a3e..000000000 --- a/infrastructure/os2display/display-api-service/etc/confd/conf.d/env.local.toml +++ /dev/null @@ -1,8 +0,0 @@ -[template] -src = "env.local.tmpl" -dest = "/var/www/html/.env.local" -owner = "www-data" -mode = "0644" -keys = [ - "/app-config" -] diff --git a/infrastructure/os2display/display-api-service/etc/confd/templates/env.local.tmpl b/infrastructure/os2display/display-api-service/etc/confd/templates/env.local.tmpl deleted file mode 100644 index 1fa420ab2..000000000 --- a/infrastructure/os2display/display-api-service/etc/confd/templates/env.local.tmpl +++ /dev/null @@ -1,71 +0,0 @@ -###> symfony/framework-bundle ### -APP_ENV={{ getenv "APP_ENV" "prod" }} -APP_SECRET={{ getenv "APP_SECRET" "MySuperSecret" }} -TRUSTED_PROXIES={{ getenv "APP_TRUSTED_PROXIES" "127.0.0.1,REMOTE_ADDR" }} -###< symfony/framework-bundle ### - -###> doctrine/doctrine-bundle ### -DATABASE_URL={{ getenv "APP_DATABASE_URL" "mysql://db:db@mariadb:3306/db?serverVersion=mariadb-10.4.0" }} -###< doctrine/doctrine-bundle ### - -###> nelmio/cors-bundle ### -CORS_ALLOW_ORIGIN={{ getenv "APP_CORS_ALLOW_ORIGIN" "'^https?://localhost(:[0-9]+)?$'" }} -###< nelmio/cors-bundle ### - -###> App ### -APP_DEFAULT_DATE_FORMAT='{{ getenv "APP_DEFAULT_DATE_FORMAT" "Y-m-d\\TH:i:s\\Z" }}' -APP_ACTIVATION_CODE_EXPIRE_INTERNAL='{{ getenv "APP_ACTIVATION_CODE_EXPIRE_INTERNAL" "P2D" }}' -###< App ### - -###> lexik/jwt-authentication-bundle ### -JWT_PASSPHRASE={{ getenv "APP_JWT_PASSPHRASE" }} -JWT_TOKEN_TTL={{ getenv "APP_JWT_TOKEN_TTL" "3600" }} -JWT_SCREEN_TOKEN_TTL={{ getenv "APP_JWT_SCREEN_TOKEN_TTL" "3600" }} -###< lexik/jwt-authentication-bundle ### - -###> gesdinet/jwt-refresh-token-bundle ### -JWT_REFRESH_TOKEN_TTL={{ getenv "APP_JWT_REFRESH_TOKEN_TTL" "2592000" }} -JWT_SCREEN_REFRESH_TOKEN_TTL={{ getenv "APP_JWT_SCREEN_REFRESH_TOKEN_TTL" "2592000" }} -###< gesdinet/jwt-refresh-token-bundle ### - -###> itk-dev/openid-connect-bundle ### -# internal provider -INTERNAL_OIDC_METADATA_URL={{ getenv "APP_INTERNAL_OIDC_METADATA_URL" "" }} -INTERNAL_OIDC_CLIENT_ID={{ getenv "APP_INTERNAL_OIDC_CLIENT_ID" "" }} -INTERNAL_OIDC_CLIENT_SECRET={{ getenv "APP_INTERNAL_OIDC_CLIENT_SECRET" "" }} -INTERNAL_OIDC_REDIRECT_URI={{ getenv "APP_INTERNAL_OIDC_REDIRECT_URI" "" }} -INTERNAL_OIDC_LEEWAY={{ getenv "APP_INTERNAL_OIDC_LEEWAY" "30" }} - -# external provider -EXTERNAL_OIDC_METADATA_URL={{ getenv "APP_EXTERNAL_OIDC_METADATA_URL" "" }} -EXTERNAL_OIDC_CLIENT_ID={{ getenv "APP_EXTERNAL_OIDC_CLIENT_ID" "" }} -EXTERNAL_OIDC_CLIENT_SECRET={{ getenv "APP_EXTERNAL_OIDC_CLIENT_SECRET" "" }} -EXTERNAL_OIDC_REDIRECT_URI={{ getenv "APP_EXTERNAL_OIDC_REDIRECT_URI" "" }} -EXTERNAL_OIDC_LEEWAY={{ getenv "APP_EXTERNAL_OIDC_LEEWAY" "30" }} -EXTERNAL_OIDC_HASH_SALT={{ getenv "APP_EXTERNAL_OIDC_HASH_SALT" "" }} - -CLI_REDIRECT={{ getenv "APP_CLI_REDIRECT" "" }} -###< itk-dev/openid-connect-bundle ### - -###> redis ### -REDIS_CACHE_PREFIX={{ getenv "APP_CLI_REDIRECT" "DisplayApiService" }} -REDIS_CACHE_DSN={{ getenv "APP_CLI_REDIRECT" "redis://redis:6379/0" }} -###< redis ### - -###> Calendar Api Feed Source ### -CALENDAR_API_FEED_SOURCE_LOCATION_ENDPOINT={{ getenv "APP_CALENDAR_API_FEED_SOURCE_LOCATION_ENDPOINT" "" }} -CALENDAR_API_FEED_SOURCE_RESOURCE_ENDPOINT={{ getenv "APP_CALENDAR_API_FEED_SOURCE_RESOURCE_ENDPOINT" "" }} -CALENDAR_API_FEED_SOURCE_EVENT_ENDPOINT={{ getenv "APP_CALENDAR_API_FEED_SOURCE_EVENT_ENDPOINT" "" }} -CALENDAR_API_FEED_SOURCE_CUSTOM_MAPPINGS={{ getenv "APP_CALENDAR_API_FEED_SOURCE_CUSTOM_MAPPINGS" "'{}'" }} -CALENDAR_API_FEED_SOURCE_EVENT_MODIFIERS={{ getenv "APP_CALENDAR_API_FEED_SOURCE_EVENT_MODIFIERS" "'{}'" }} -CALENDAR_API_FEED_SOURCE_DATE_FORMAT={{ getenv "APP_CALENDAR_API_FEED_SOURCE_DATE_FORMAT" "" }} -CALENDAR_API_FEED_SOURCE_DATE_TIMEZONE={{ getenv "APP_CALENDAR_API_FEED_SOURCE_DATE_TIMEZONE" "" }} -CALENDAR_API_FEED_SOURCE_CACHE_EXPIRE_SECONDS={{ getenv "CALENDAR_API_FEED_SOURCE_CACHE_EXPIRE_SECONDS" "300" }} -###< Calendar Api Feed Source ### - -EVENTDATABASE_API_V2_CACHE_EXPIRE_SECONDS={{ getenv "APP_EVENTDATABASE_API_V2_CACHE_EXPIRE_SECONDS" "300" }} - -TRACK_SCREEN_INFO={{ getenv "APP_TRACK_SCREEN_INFO" "false" }} -TRACK_SCREEN_INFO_UPDATE_INTERVAL_SECONDS={{ getenv "APP_TRACK_SCREEN_INFO_UPDATE_INTERVAL_SECONDS" "300" }} - -APP_KEY_VAULT_JSON={{ getenv "APP_KEY_VAULT_JSON" "{}" }} diff --git a/infrastructure/os2display/display-api-service/gitconfig b/infrastructure/os2display/display-api-service/gitconfig deleted file mode 100644 index 60a3b0202..000000000 --- a/infrastructure/os2display/display-api-service/gitconfig +++ /dev/null @@ -1,9 +0,0 @@ -[color] - ui = true - -[alias] - branch-name = !git for-each-ref --format='%(refname:short)' `git symbolic-ref HEAD` - lg = log --graph --pretty=format:'%Cred%h%Creset %Cgreen(%cr) -%C(yellow)%d%Creset %s %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative - -[safe] - directory = * \ No newline at end of file diff --git a/infrastructure/os2display/display-api-service/php/apcu.ini b/infrastructure/os2display/display-api-service/php/apcu.ini deleted file mode 100644 index 86164be82..000000000 --- a/infrastructure/os2display/display-api-service/php/apcu.ini +++ /dev/null @@ -1,7 +0,0 @@ -extension=apcu -apc.enabled=${PHP_APCU_ENABLED} -apc.shm_segments=${PHP_APCU_SEGMENTS} -apc.shm_size=${PHP_APCU_MEMORY_SIZE} - -apc.enable_cli=${PHP_APCU_ENABLED_CLI} -apc.preload_path=${PHP_APCU_PRELOAD_PATH} \ No newline at end of file diff --git a/infrastructure/os2display/display-api-service/php/fpm.ini b/infrastructure/os2display/display-api-service/php/fpm.ini deleted file mode 100644 index 49e0aeb48..000000000 --- a/infrastructure/os2display/display-api-service/php/fpm.ini +++ /dev/null @@ -1,17 +0,0 @@ -[www] -pm = ${PHP_PM_TYPE} -pm.max_children = ${PHP_PM_MAX_CHILDREN} -pm.start_servers = ${PHP_PM_START_SERVERS} -pm.min_spare_servers = ${PHP_PM_MIN_SPARE_SERVERS} -pm.max_spare_servers = ${PHP_PM_MAX_SPARE_SERVERS} -pm.max_requests = ${PHP_PM_MAX_REQUESTS} - -request_slowlog_timeout = ${PHP_REQUEST_SLOWLOG_TIMEOUT} -slowlog = ${PHP_SLOWLOG} - -; Enable the FPM status page -pm.status_path = /status - -user = ${PHP_USER} -group = ${PHP_GROUP} - diff --git a/infrastructure/os2display/display-api-service/php/opcache.ini b/infrastructure/os2display/display-api-service/php/opcache.ini deleted file mode 100644 index 8ff178634..000000000 --- a/infrastructure/os2display/display-api-service/php/opcache.ini +++ /dev/null @@ -1,15 +0,0 @@ -zend_extension=opcache.so - -[opcache] -opcache.jit=${PHP_OPCACHE_JIT} - -opcache.enable=${PHP_OPCACHE_ENABLED} -opcache.revalidate_freq=${PHP_OPCACHE_REVALIDATE_FREQ} -opcache.validate_timestamps=${PHP_OPCACHE_VALIDATE_TIMESTAMPS} -opcache.max_accelerated_files=${PHP_OPCACHE_MAX_ACCELERATED_FILES} -opcache.memory_consumption=${PHP_OPCACHE_MEMORY_CONSUMPTION} -opcache.max_wasted_percentage=${PHP_OPCACHE_MAX_WASTED_PERCENTAGE} -opcache.interned_strings_buffer=16 -opcache.fast_shutdown=1 - -opcache.optimization_level=0xFFFFFFEF diff --git a/infrastructure/os2display/display-api-service/php/php.ini b/infrastructure/os2display/display-api-service/php/php.ini deleted file mode 100644 index 2bfde2b40..000000000 --- a/infrastructure/os2display/display-api-service/php/php.ini +++ /dev/null @@ -1,13 +0,0 @@ -realpath_cache_size = 4096k -realpath_cache_ttl = 600 - -expose_php = Off -max_execution_time = ${PHP_MAX_EXECUTION_TIME} -memory_limit = ${PHP_MEMORY_LIMIT} - -post_max_size = ${PHP_POST_MAX_SIZE} -upload_max_filesize = ${PHP_UPLOAD_MAX_FILESIZE} - -date.timezone = ${PHP_TIMEZONE} - -sendmail_path = ${PHP_SENDMAIL_PATH} diff --git a/infrastructure/os2display/nginx/Dockerfile b/infrastructure/os2display/nginx/Dockerfile deleted file mode 100644 index bc13dcd12..000000000 --- a/infrastructure/os2display/nginx/Dockerfile +++ /dev/null @@ -1,37 +0,0 @@ -ARG APP_VERSION="develop" -FROM os2display/display-api-service:${APP_VERSION} as APPLICATION - -FROM nginxinc/nginx-unprivileged:alpine -LABEL maintainer="ITK Dev " -ARG UID=101 -ARG GID=101 - -ENV APP_PATH=/var/www/html - -USER root - -RUN mkdir -p ${APP_PATH}/public - -COPY --from=APPLICATION ${APP_PATH}/public ${APP_PATH}/public - -WORKDIR ${APP_PATH} - -# Copy configuration. -COPY etc /etc/ - -# Install configuration template handler -ADD https://github.com/kelseyhightower/confd/releases/download/v0.16.0/confd-0.16.0-linux-amd64 /usr/local/bin/confd -RUN chmod +x /usr/local/bin/confd - -COPY docker-entrypoint.sh / -RUN chmod +x /docker-entrypoint.sh \ - && chown -R $UID:0 ${APP_PATH} \ - && chmod -R g+w ${APP_PATH} - -USER $UID - -EXPOSE 8080 - -ENTRYPOINT [ "/docker-entrypoint.sh" ] - -CMD ["nginx", "-g", "daemon off;"] diff --git a/infrastructure/os2display/nginx/docker-entrypoint.sh b/infrastructure/os2display/nginx/docker-entrypoint.sh deleted file mode 100644 index 978fa9f4e..000000000 --- a/infrastructure/os2display/nginx/docker-entrypoint.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/sh - -set -eux - -## Run templates with configuration. -/usr/local/bin/confd --onetime --backend env --confdir /etc/confd - -entrypoint_log() { - if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then - echo "$@" - fi -} - -if [ "$1" = "nginx" -o "$1" = "nginx-debug" ]; then - if /usr/bin/find "/docker-entrypoint.d/" -mindepth 1 -maxdepth 1 -type f -print -quit 2>/dev/null | read v; then - entrypoint_log "$0: /docker-entrypoint.d/ is not empty, will attempt to perform configuration" - - entrypoint_log "$0: Looking for shell scripts in /docker-entrypoint.d/" - find "/docker-entrypoint.d/" -follow -type f -print | sort -V | while read -r f; do - case "$f" in - *.envsh) - if [ -x "$f" ]; then - entrypoint_log "$0: Sourcing $f"; - . "$f" - else - # warn on shell scripts without exec bit - entrypoint_log "$0: Ignoring $f, not executable"; - fi - ;; - *.sh) - if [ -x "$f" ]; then - entrypoint_log "$0: Launching $f"; - "$f" - else - # warn on shell scripts without exec bit - entrypoint_log "$0: Ignoring $f, not executable"; - fi - ;; - *) entrypoint_log "$0: Ignoring $f";; - esac - done - - entrypoint_log "$0: Configuration complete; ready for start up" - else - entrypoint_log "$0: No files found in /docker-entrypoint.d/, skipping configuration" - fi -fi - -exec "$@" diff --git a/infrastructure/os2display/nginx/etc/confd/conf.d/default.conf.toml b/infrastructure/os2display/nginx/etc/confd/conf.d/default.conf.toml deleted file mode 100644 index 185954f0d..000000000 --- a/infrastructure/os2display/nginx/etc/confd/conf.d/default.conf.toml +++ /dev/null @@ -1,7 +0,0 @@ -[template] -src = "default.conf.tmpl" -dest = "/etc/nginx/conf.d/default.conf" -mode = "0644" -keys = [ - "/nginx-config" -] diff --git a/infrastructure/os2display/nginx/etc/confd/conf.d/nginx.conf.toml b/infrastructure/os2display/nginx/etc/confd/conf.d/nginx.conf.toml deleted file mode 100644 index 7c81a661f..000000000 --- a/infrastructure/os2display/nginx/etc/confd/conf.d/nginx.conf.toml +++ /dev/null @@ -1,7 +0,0 @@ -[template] -src = "nginx.conf.tmpl" -dest = "/etc/nginx/nginx.conf" -mode = "0644" -keys = [ - "/nginx-config" -] diff --git a/infrastructure/os2display/nginx/etc/confd/templates/default.conf.tmpl b/infrastructure/os2display/nginx/etc/confd/templates/default.conf.tmpl deleted file mode 100644 index b6f32099b..000000000 --- a/infrastructure/os2display/nginx/etc/confd/templates/default.conf.tmpl +++ /dev/null @@ -1,52 +0,0 @@ -# @see https://symfony.com/doc/current/setup/web_server_configuration.html -server { - listen 8080; - server_name localhost; - root /var/www/html/public; - - location / { - add_header X-Robots-Tag "noindex, nofollow, nosnippet, noarchive"; - - # try to serve file directly, fallback to index.php - try_files $uri /index.php$is_args$args; - } - - location = /robots.txt { - add_header Content-Type text/plain; - add_header X-Robots-Tag "noindex, nofollow, nosnippet, noarchive"; - return 200 "User-agent: *\nDisallow: /\n"; - } - - location ~ ^/index\.php(/|$) { - fastcgi_pass {{ getenv "PHP_FPM_SERVER" "phpfpm" }}:{{ getenv "PHP_FPM_SERVER_PORT" "9000" }}; - fastcgi_split_path_info ^(.+\.php)(/.*)$; - include fastcgi_params; - - fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; - fastcgi_param DOCUMENT_ROOT $realpath_root; - - internal; - } - - # return 404 for all other php files not matching the front controller - # this prevents access to other php files you don't want to be accessible. - location ~ \.php$ { - return 404; - } - - error_log /var/log/nginx/error.log; - access_log /var/log/nginx/access.log; -} - -server { - listen 8080; - server_name localhost; - root /var/www/html/public; - - error_log off; - access_log off; - - location /stub_status { - stub_status; - } -} diff --git a/infrastructure/os2display/nginx/etc/confd/templates/nginx.conf.tmpl b/infrastructure/os2display/nginx/etc/confd/templates/nginx.conf.tmpl deleted file mode 100644 index 254adf3ec..000000000 --- a/infrastructure/os2display/nginx/etc/confd/templates/nginx.conf.tmpl +++ /dev/null @@ -1,51 +0,0 @@ -worker_processes auto; - -error_log /var/log/nginx/error.log warn; -pid /tmp/nginx.pid; - -events { - worker_connections 2048; - multi_accept on; -} - - -http { - open_file_cache max=10000 inactive=5m; - open_file_cache_valid 5m; - open_file_cache_min_uses 5; - open_file_cache_errors off; - - sendfile on; - tcp_nopush on; - tcp_nodelay on; - keepalive_timeout 15 15; - types_hash_max_size 2048; - - server_tokens off; - - client_max_body_size {{ getenv "NGINX_FPM_UPLOAD_MAX" "8M" }}; - - gzip on; - gzip_disable "msie6"; - gzip_vary on; - gzip_proxied any; - gzip_comp_level 6; - gzip_buffers 16 8k; - gzip_http_version 1.0; - gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript; - - include /etc/nginx/mime.types; - default_type application/octet-stream; - - set_real_ip_from 172.16.0.0/8; - real_ip_recursive on; - real_ip_header X-Forwarded-For; - - log_format main '$http_x_real_ip - $remote_user [$time_local] "$request" ' - '$status $body_bytes_sent "$http_referer" ' - '"$http_user_agent" "$http_x_forwarded_for"'; - error_log /dev/stderr; - access_log /dev/stdout main; - - include /etc/nginx/conf.d/*.conf; -} diff --git a/infrastructure/os2display/run.sh b/infrastructure/os2display/run.sh deleted file mode 100755 index ae23d67b4..000000000 --- a/infrastructure/os2display/run.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh - -set -eux - -APP_VERSION=develop - -docker pull nginxinc/nginx-unprivileged:alpine - -docker build --pull --no-cache --build-arg APP_VERSION=${APP_VERSION} --tag=os2display/display-api-service:${APP_VERSION} --file="display-api-service/Dockerfile" display-api-service -docker build --no-cache --build-arg VERSION=${APP_VERSION} --tag=os2display/display-api-service-nginx:${APP_VERSION} --file="nginx/Dockerfile" nginx - -# docker push os2display/display-api-service:${APP_VERSION} -# docker push os2display/display-api-service-nginx:${APP_VERSION} From a4fd679f5ef35525cb9d85891c8a7cf4515c3bf6 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Fri, 5 Sep 2025 11:23:16 +0200 Subject: [PATCH 02/59] 5402: Refacktor itk-dev images to single infrastructure. Rename itk-dev images to os2display --- infrastructure/{itkdev => }/Readme.md | 2 +- infrastructure/{itkdev => }/display-api-service/Dockerfile | 2 +- .../{itkdev => }/display-api-service/docker-entrypoint.sh | 0 .../display-api-service/etc/confd/conf.d/env.local.toml | 0 .../display-api-service/etc/confd/templates/env.local.tmpl | 0 infrastructure/{itkdev => }/nginx/Dockerfile | 2 +- infrastructure/{itkdev => }/nginx/docker-entrypoint.sh | 0 .../{itkdev => }/nginx/etc/confd/conf.d/default.conf.toml | 0 .../{itkdev => }/nginx/etc/confd/conf.d/nginx.conf.toml | 0 .../nginx/etc/confd/templates/default.conf.tmpl | 0 .../{itkdev => }/nginx/etc/confd/templates/nginx.conf.tmpl | 0 infrastructure/package-lock.json | 6 ------ infrastructure/{itkdev => }/run.sh | 0 13 files changed, 3 insertions(+), 9 deletions(-) rename infrastructure/{itkdev => }/Readme.md (81%) rename infrastructure/{itkdev => }/display-api-service/Dockerfile (97%) rename infrastructure/{itkdev => }/display-api-service/docker-entrypoint.sh (100%) rename infrastructure/{itkdev => }/display-api-service/etc/confd/conf.d/env.local.toml (100%) rename infrastructure/{itkdev => }/display-api-service/etc/confd/templates/env.local.tmpl (100%) rename infrastructure/{itkdev => }/nginx/Dockerfile (98%) rename infrastructure/{itkdev => }/nginx/docker-entrypoint.sh (100%) rename infrastructure/{itkdev => }/nginx/etc/confd/conf.d/default.conf.toml (100%) rename infrastructure/{itkdev => }/nginx/etc/confd/conf.d/nginx.conf.toml (100%) rename infrastructure/{itkdev => }/nginx/etc/confd/templates/default.conf.tmpl (100%) rename infrastructure/{itkdev => }/nginx/etc/confd/templates/nginx.conf.tmpl (100%) delete mode 100644 infrastructure/package-lock.json rename infrastructure/{itkdev => }/run.sh (100%) diff --git a/infrastructure/itkdev/Readme.md b/infrastructure/Readme.md similarity index 81% rename from infrastructure/itkdev/Readme.md rename to infrastructure/Readme.md index abf2b9d1e..17978bc45 100644 --- a/infrastructure/itkdev/Readme.md +++ b/infrastructure/Readme.md @@ -1,3 +1,3 @@ # ITK-development image build -This folder contains the infrastructure files for building the `itkdev/*` images +This folder contains the infrastructure files for building the `os2display/*` images diff --git a/infrastructure/itkdev/display-api-service/Dockerfile b/infrastructure/display-api-service/Dockerfile similarity index 97% rename from infrastructure/itkdev/display-api-service/Dockerfile rename to infrastructure/display-api-service/Dockerfile index b30ecff1a..5e9c835dd 100644 --- a/infrastructure/itkdev/display-api-service/Dockerfile +++ b/infrastructure/display-api-service/Dockerfile @@ -1,4 +1,4 @@ -FROM itkdev/php8.3-fpm:alpine AS APP_BUILDER +FROM itkdev/php8.4-fpm:alpine AS APP_BUILDER LABEL maintainer="ITK Dev " ARG APP_VERSION="develop" diff --git a/infrastructure/itkdev/display-api-service/docker-entrypoint.sh b/infrastructure/display-api-service/docker-entrypoint.sh similarity index 100% rename from infrastructure/itkdev/display-api-service/docker-entrypoint.sh rename to infrastructure/display-api-service/docker-entrypoint.sh diff --git a/infrastructure/itkdev/display-api-service/etc/confd/conf.d/env.local.toml b/infrastructure/display-api-service/etc/confd/conf.d/env.local.toml similarity index 100% rename from infrastructure/itkdev/display-api-service/etc/confd/conf.d/env.local.toml rename to infrastructure/display-api-service/etc/confd/conf.d/env.local.toml diff --git a/infrastructure/itkdev/display-api-service/etc/confd/templates/env.local.tmpl b/infrastructure/display-api-service/etc/confd/templates/env.local.tmpl similarity index 100% rename from infrastructure/itkdev/display-api-service/etc/confd/templates/env.local.tmpl rename to infrastructure/display-api-service/etc/confd/templates/env.local.tmpl diff --git a/infrastructure/itkdev/nginx/Dockerfile b/infrastructure/nginx/Dockerfile similarity index 98% rename from infrastructure/itkdev/nginx/Dockerfile rename to infrastructure/nginx/Dockerfile index 7a0334dd9..6be4be3c4 100644 --- a/infrastructure/itkdev/nginx/Dockerfile +++ b/infrastructure/nginx/Dockerfile @@ -17,7 +17,7 @@ COPY --from=APPLICATION ${APP_PATH}/public ${APP_PATH}/public WORKDIR ${APP_PATH} # Copy configuration. -COPY etc/ /etc/ +COPY etc /etc/ # Install configuration template handler ADD https://github.com/kelseyhightower/confd/releases/download/v0.16.0/confd-0.16.0-linux-amd64 /usr/local/bin/confd diff --git a/infrastructure/itkdev/nginx/docker-entrypoint.sh b/infrastructure/nginx/docker-entrypoint.sh similarity index 100% rename from infrastructure/itkdev/nginx/docker-entrypoint.sh rename to infrastructure/nginx/docker-entrypoint.sh diff --git a/infrastructure/itkdev/nginx/etc/confd/conf.d/default.conf.toml b/infrastructure/nginx/etc/confd/conf.d/default.conf.toml similarity index 100% rename from infrastructure/itkdev/nginx/etc/confd/conf.d/default.conf.toml rename to infrastructure/nginx/etc/confd/conf.d/default.conf.toml diff --git a/infrastructure/itkdev/nginx/etc/confd/conf.d/nginx.conf.toml b/infrastructure/nginx/etc/confd/conf.d/nginx.conf.toml similarity index 100% rename from infrastructure/itkdev/nginx/etc/confd/conf.d/nginx.conf.toml rename to infrastructure/nginx/etc/confd/conf.d/nginx.conf.toml diff --git a/infrastructure/itkdev/nginx/etc/confd/templates/default.conf.tmpl b/infrastructure/nginx/etc/confd/templates/default.conf.tmpl similarity index 100% rename from infrastructure/itkdev/nginx/etc/confd/templates/default.conf.tmpl rename to infrastructure/nginx/etc/confd/templates/default.conf.tmpl diff --git a/infrastructure/itkdev/nginx/etc/confd/templates/nginx.conf.tmpl b/infrastructure/nginx/etc/confd/templates/nginx.conf.tmpl similarity index 100% rename from infrastructure/itkdev/nginx/etc/confd/templates/nginx.conf.tmpl rename to infrastructure/nginx/etc/confd/templates/nginx.conf.tmpl diff --git a/infrastructure/package-lock.json b/infrastructure/package-lock.json deleted file mode 100644 index 4ca926f5d..000000000 --- a/infrastructure/package-lock.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "app", - "lockfileVersion": 3, - "requires": true, - "packages": {} -} diff --git a/infrastructure/itkdev/run.sh b/infrastructure/run.sh similarity index 100% rename from infrastructure/itkdev/run.sh rename to infrastructure/run.sh From 6901548d6b0d8a54b5a0471c3f6028fe8c9e6ec2 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Fri, 5 Sep 2025 11:24:39 +0200 Subject: [PATCH 03/59] 5402: Update github action image uild jobs for new infrastructure --- ...l => docker_build_images_from_develop.yml} | 18 +++--- ...g.yml => docker_build_images_from_tag.yml} | 18 +++--- .../os2display_docker_build_develop.yml | 60 ------------------ .../workflows/os2display_docker_build_tag.yml | 63 ------------------- 4 files changed, 18 insertions(+), 141 deletions(-) rename .github/workflows/{itkdev_docker_build_develop.yml => docker_build_images_from_develop.yml} (71%) rename .github/workflows/{itkdev_docker_build_tag.yml => docker_build_images_from_tag.yml} (73%) delete mode 100644 .github/workflows/os2display_docker_build_develop.yml delete mode 100644 .github/workflows/os2display_docker_build_tag.yml diff --git a/.github/workflows/itkdev_docker_build_develop.yml b/.github/workflows/docker_build_images_from_develop.yml similarity index 71% rename from .github/workflows/itkdev_docker_build_develop.yml rename to .github/workflows/docker_build_images_from_develop.yml index aaef33621..bcf6cd50f 100644 --- a/.github/workflows/itkdev_docker_build_develop.yml +++ b/.github/workflows/docker_build_images_from_develop.yml @@ -4,8 +4,8 @@ on: branches: - "develop" -# This Action builds to itkdev/* using ./infrastructure/itkdev/* -name: ITK Dev - Build docker image (develop) +# This Action builds to os2display/* using ./infrastructure/* +name: Build docker image (develop) jobs: docker: @@ -15,7 +15,7 @@ jobs: COMPOSER_ALLOW_SUPERUSER: 1 steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Login to DockerHub uses: docker/login-action@v3 @@ -28,13 +28,13 @@ jobs: id: meta-api uses: docker/metadata-action@v5 with: - images: itkdev/os2display-api-service + images: os2display/os2display-api-service - name: Build and push (API) uses: docker/build-push-action@v5 with: - context: ./infrastructure/itkdev/display-api-service/ - file: ./infrastructure/itkdev/display-api-service/Dockerfile + context: ./infrastructure/display-api-service/ + file: ./infrastructure/display-api-service/Dockerfile build-args: | VERSION=${{ env.APP_VERSION }} push: true @@ -46,13 +46,13 @@ jobs: id: meta-nginx uses: docker/metadata-action@v5 with: - images: itkdev/os2display-api-service-nginx + images: os2display/os2display-api-service-nginx - name: Build and push (Nginx) uses: docker/build-push-action@v5 with: - context: ./infrastructure/itkdev/nginx/ - file: ./infrastructure/itkdev/nginx/Dockerfile + context: ./infrastructure/nginx/ + file: ./infrastructure/nginx/Dockerfile build-args: | APP_VERSION=${{ env.APP_VERSION }} push: true diff --git a/.github/workflows/itkdev_docker_build_tag.yml b/.github/workflows/docker_build_images_from_tag.yml similarity index 73% rename from .github/workflows/itkdev_docker_build_tag.yml rename to .github/workflows/docker_build_images_from_tag.yml index f64644a28..208a41b61 100644 --- a/.github/workflows/itkdev_docker_build_tag.yml +++ b/.github/workflows/docker_build_images_from_tag.yml @@ -4,8 +4,8 @@ on: tags: - "*" -# This Action builds to itkdev/* using ./infrastructure/itkdev/* -name: ITK Dev - Build docker image (tag) +# This Action builds to os2display/* using ./infrastructure/* +name: Build docker image (tag) jobs: docker: @@ -14,7 +14,7 @@ jobs: COMPOSER_ALLOW_SUPERUSER: 1 steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Login to DockerHub uses: docker/login-action@v3 @@ -27,13 +27,13 @@ jobs: id: meta-api uses: docker/metadata-action@v5 with: - images: itkdev/os2display-api-service + images: os2display/os2display-api-service - name: Build and push (API) uses: docker/build-push-action@v5 with: - context: ./infrastructure/itkdev/display-api-service/ - file: ./infrastructure/itkdev/display-api-service/Dockerfile + context: ./infrastructure/display-api-service/ + file: ./infrastructure/display-api-service/Dockerfile build-args: | APP_VERSION=${{ github.ref }} push: true @@ -45,7 +45,7 @@ jobs: id: meta-nginx uses: docker/metadata-action@v5 with: - images: itkdev/os2display-api-service-nginx + images: os2display/os2display-api-service-nginx - name: Get the tag id: get_tag @@ -54,8 +54,8 @@ jobs: - name: Build and push (Nginx) uses: docker/build-push-action@v5 with: - context: ./infrastructure/itkdev/nginx/ - file: ./infrastructure/itkdev/nginx/Dockerfile + context: ./infrastructure/nginx/ + file: ./infrastructure/nginx/Dockerfile build-args: | APP_VERSION=${{ steps.get_tag.outputs.git_tag }} push: true diff --git a/.github/workflows/os2display_docker_build_develop.yml b/.github/workflows/os2display_docker_build_develop.yml deleted file mode 100644 index bcdf09a6c..000000000 --- a/.github/workflows/os2display_docker_build_develop.yml +++ /dev/null @@ -1,60 +0,0 @@ ---- -on: - push: - branches: - - "develop" - -# This Action builds to os2display/* using ./infrastructure/os2display/* -name: OS2display - Build docker image (develop) - -jobs: - docker: - runs-on: ubuntu-latest - env: - APP_VERSION: develop - COMPOSER_ALLOW_SUPERUSER: 1 - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Login to DockerHub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USER }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - # Build api - - name: Docker meta (API) - id: meta-api - uses: docker/metadata-action@v5 - with: - images: os2display/display-api-service - - - name: Build and push (API) - uses: docker/build-push-action@v5 - with: - context: ./infrastructure/os2display/display-api-service/ - file: ./infrastructure/os2display/display-api-service/Dockerfile - build-args: | - VERSION=${{ env.APP_VERSION }} - push: true - tags: ${{ steps.meta-api.outputs.tags }} - labels: ${{ steps.meta-api.outputs.labels }} - - # Build nginx (depends on api build) - - name: Docker meta (Nginx) - id: meta-nginx - uses: docker/metadata-action@v5 - with: - images: os2display/display-api-service-nginx - - - name: Build and push (Nginx) - uses: docker/build-push-action@v5 - with: - context: ./infrastructure/os2display/nginx/ - file: ./infrastructure/os2display/nginx/Dockerfile - build-args: | - APP_VERSION=${{ env.APP_VERSION }} - push: true - tags: ${{ steps.meta-nginx.outputs.tags }} - labels: ${{ steps.meta-nginx.outputs.labels }} diff --git a/.github/workflows/os2display_docker_build_tag.yml b/.github/workflows/os2display_docker_build_tag.yml deleted file mode 100644 index 8bd2b12aa..000000000 --- a/.github/workflows/os2display_docker_build_tag.yml +++ /dev/null @@ -1,63 +0,0 @@ ---- -on: - push: - tags: - - "*" - -# This Action builds to os2display/* using ./infrastructure/os2display/* -name: OS2display - Build docker image (tag) - -jobs: - docker: - runs-on: ubuntu-latest - env: - COMPOSER_ALLOW_SUPERUSER: 1 - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Login to DockerHub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USER }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - # Build api - - name: Docker meta (API) - id: meta-api - uses: docker/metadata-action@v5 - with: - images: os2display/display-api-service - - - name: Build and push (API) - uses: docker/build-push-action@v5 - with: - context: ./infrastructure/os2display/display-api-service/ - file: ./infrastructure/os2display/display-api-service/Dockerfile - build-args: | - APP_VERSION=${{ github.ref }} - push: true - tags: ${{ steps.meta-api.outputs.tags }} - labels: ${{ steps.meta-api.outputs.labels }} - - # Build nginx (depends on api build) - - name: Docker meta (Nginx) - id: meta-nginx - uses: docker/metadata-action@v5 - with: - images: os2display/display-api-service-nginx - - - name: Get the tag - id: get_tag - run: echo ::set-output name=git_tag::$(echo $GITHUB_REF_NAME) - - - name: Build and push (Nginx) - uses: docker/build-push-action@v5 - with: - context: ./infrastructure/os2display/nginx/ - file: ./infrastructure/os2display/nginx/Dockerfile - build-args: | - APP_VERSION=${{ steps.get_tag.outputs.git_tag }} - push: true - tags: ${{ steps.meta-nginx.outputs.tags }} - labels: ${{ steps.meta-nginx.outputs.labels }} From 2b84d3255b150d87f87b7bee7d58435bd178c36c Mon Sep 17 00:00:00 2001 From: turegjorup Date: Mon, 3 Nov 2025 13:03:56 +0100 Subject: [PATCH 04/59] 5402: Update infrastructure for 3.0 --- .dockerignore | 68 ++++++++++++++++++ .github/Taskfile.yml | 46 ++++++++++++ .../docker_build_images_from_develop.yml | 23 ++++-- .../docker_build_images_from_tag.yml | 21 +++--- .github/workflows/github_build_release.yml | 20 +++--- .gitignore | 2 - docker-compose.server.yml | 2 +- docker-compose.yml | 2 +- infrastructure/build-n-push.sh | 27 +++++++ infrastructure/display-api-service/Dockerfile | 59 ++++++++------- .../display-api-service/docker-entrypoint.sh | 6 +- .../etc/confd/conf.d/env.local.toml | 8 --- .../etc/confd/templates/env.local.tmpl | 71 ------------------- infrastructure/nginx/Dockerfile | 45 +++++++++--- .../etc/confd/templates/default.conf.tmpl | 23 ++++++ infrastructure/run.sh | 13 ---- 16 files changed, 273 insertions(+), 163 deletions(-) create mode 100644 .dockerignore create mode 100644 .github/Taskfile.yml create mode 100755 infrastructure/build-n-push.sh delete mode 100644 infrastructure/display-api-service/etc/confd/conf.d/env.local.toml delete mode 100644 infrastructure/display-api-service/etc/confd/templates/env.local.tmpl delete mode 100755 infrastructure/run.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..7e18bc03c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,68 @@ +/.git +!/.git/config + +# Unneeded application files +/.docker/data/README.md +/.github +/docs +/fixtures +/infrastructure +/public/build +/public/fixtures +public/build +public/fixtures +public/media/* +!public/media/thumbnail_other.png +!public/media/thumbnail_video.png +/scripts +/tests + +###> symfony/framework-bundle ### +/.env.local +/.env.local.php +/.env.*.local +/config/secrets/prod/prod.decrypt.private.php +/var/ +/vendor/ +###< symfony/framework-bundle ### + +###> friendsofphp/php-cs-fixer ### +/.php-cs-fixer.php +/.php-cs-fixer.cache +###< friendsofphp/php-cs-fixer ### + +###> phpunit/phpunit ### +/phpunit.xml +.phpunit.result.cache +###< phpunit/phpunit ### + +###> lexik/jwt-authentication-bundle ### +/config/jwt/*.pem +###< lexik/jwt-authentication-bundle ### + +xdebug.ini +launch.json + +###> liip/imagine-bundle ### +/public/media/cache/ +###< liip/imagine-bundle ### + +###> phpstan/phpstan ### +phpstan.neon +###< phpstan/phpstan ### + +###> pentatrion/vite-bundle ### +/node_modules/ +/public/build/ +###< pentatrion/vite-bundle ### + +#> Playwright +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ +#< Playwright + +###> vincentlanglet/twig-cs-fixer ### +/.twig-cs-fixer.cache +###< vincentlanglet/twig-cs-fixer ### diff --git a/.github/Taskfile.yml b/.github/Taskfile.yml new file mode 100644 index 000000000..2cb169d48 --- /dev/null +++ b/.github/Taskfile.yml @@ -0,0 +1,46 @@ +# Task file for GitHub Actions, https://taskfile.dev/ + +version: "3" + +# https://taskfile.dev/usage/#env-files +dotenv: [".env.local", ".env"] + +tasks: + default: + desc: "List all tasks" + cmds: + - task --list-all + silent: true + + build-prod: + desc: "Build application for production" + cmds: + - task setup-network + - task composer-install + - task npm-install + - task install-cleanup + + setup-network: + desc: "Setup docker frontend network" + cmds: + - docker network create frontend + + composer-install: + desc: "Install dependencies with composer." + cmds: + - docker compose run --rm --env APP_ENV=prod phpfpm composer install --no-dev -o --classmap-authoritative + - docker compose run --rm --env APP_ENV=prod phpfpm composer clear-cache + + npm-install: + desc: "Installs node dependencies with npm." + cmds: + - docker compose run --rm node npm install + - docker compose run --rm node npm run build + + install-cleanup: + desc: "Cleanup after install" + cmds: + - rm -rf infrastructure + - rm -rf fixtures + - rm -rf tests + - rm -rf node_modules diff --git a/.github/workflows/docker_build_images_from_develop.yml b/.github/workflows/docker_build_images_from_develop.yml index bcf6cd50f..0becba3b0 100644 --- a/.github/workflows/docker_build_images_from_develop.yml +++ b/.github/workflows/docker_build_images_from_develop.yml @@ -14,24 +14,27 @@ jobs: APP_VERSION: develop COMPOSER_ALLOW_SUPERUSER: 1 steps: - - name: Checkout - uses: actions/checkout@v5 - - name: Login to DockerHub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USER }} password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + # Build api - name: Docker meta (API) id: meta-api uses: docker/metadata-action@v5 with: - images: os2display/os2display-api-service + images: os2display/display-api-service - name: Build and push (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ./infrastructure/display-api-service/ file: ./infrastructure/display-api-service/Dockerfile @@ -40,16 +43,19 @@ jobs: push: true tags: ${{ steps.meta-api.outputs.tags }} labels: ${{ steps.meta-api.outputs.labels }} + provenance: mode=max + sbom: true + platforms: linux/amd64,linux/arm64 # Build nginx (depends on api build) - name: Docker meta (Nginx) id: meta-nginx uses: docker/metadata-action@v5 with: - images: os2display/os2display-api-service-nginx + images: os2display/display-api-service-nginx - name: Build and push (Nginx) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ./infrastructure/nginx/ file: ./infrastructure/nginx/Dockerfile @@ -58,3 +64,6 @@ jobs: push: true tags: ${{ steps.meta-nginx.outputs.tags }} labels: ${{ steps.meta-nginx.outputs.labels }} + provenance: mode=max + sbom: true + platforms: linux/amd64,linux/arm64 diff --git a/.github/workflows/docker_build_images_from_tag.yml b/.github/workflows/docker_build_images_from_tag.yml index 208a41b61..4b441be89 100644 --- a/.github/workflows/docker_build_images_from_tag.yml +++ b/.github/workflows/docker_build_images_from_tag.yml @@ -10,27 +10,28 @@ name: Build docker image (tag) jobs: docker: runs-on: ubuntu-latest - env: - COMPOSER_ALLOW_SUPERUSER: 1 steps: - - name: Checkout - uses: actions/checkout@v5 - - name: Login to DockerHub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USER }} password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + # Build api - name: Docker meta (API) id: meta-api uses: docker/metadata-action@v5 with: - images: os2display/os2display-api-service + images: os2display/display-api-service - name: Build and push (API) - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ./infrastructure/display-api-service/ file: ./infrastructure/display-api-service/Dockerfile @@ -39,13 +40,15 @@ jobs: push: true tags: ${{ steps.meta-api.outputs.tags }} labels: ${{ steps.meta-api.outputs.labels }} + provenance: mode=max + sbom: true # Build nginx (depends on api build) - name: Docker meta (Nginx) id: meta-nginx uses: docker/metadata-action@v5 with: - images: os2display/os2display-api-service-nginx + images: os2display/display-api-service-nginx - name: Get the tag id: get_tag @@ -61,3 +64,5 @@ jobs: push: true tags: ${{ steps.meta-nginx.outputs.tags }} labels: ${{ steps.meta-nginx.outputs.labels }} + provenance: mode=max + sbom: true diff --git a/.github/workflows/github_build_release.yml b/.github/workflows/github_build_release.yml index 2ca6b44d7..810ab96e3 100644 --- a/.github/workflows/github_build_release.yml +++ b/.github/workflows/github_build_release.yml @@ -8,23 +8,23 @@ name: Create Github Release permissions: contents: write +env: + COMPOSE_USER: runner + jobs: create-release: runs-on: ubuntu-latest - env: - COMPOSER_ALLOW_SUPERUSER: 1 - APP_ENV: prod steps: - name: Checkout uses: actions/checkout@v4 - - name: Composer install - run: | - docker network create frontend - docker compose run --rm --user=root --env APP_ENV=prod phpfpm composer install --no-dev -o --classmap-authoritative - docker compose run --rm --user=root --env APP_ENV=prod phpfpm composer clear-cache - rm -rf infrastructure + - name: Install Task task runner + uses: go-task/setup-task@v1 + - name: Install, Build, Cleanup + run: | + task --taskfile=.github/Taskfile.yaml build-prod + - name: Make assets dir run: | mkdir -p ../assets @@ -39,7 +39,7 @@ jobs: - name: Create a release in GitHub and uploads assets run: | - gh release create ${{ github.ref_name }} --verify-tag --generate-notes ../assets/*.* + gh release create ${{ github.ref_name }} --verify-tag --generate-notes ${{ steps.prerelease.outputs.flag }} ../assets/*.* env: GITHUB_TOKEN: ${{ github.TOKEN }} shell: bash diff --git a/.gitignore b/.gitignore index 6a75d6466..521b8cb58 100644 --- a/.gitignore +++ b/.gitignore @@ -25,8 +25,6 @@ /.php-cs-fixer.cache ###< friendsofphp/php-cs-fixer ### -node_modules - public/media/* !public/media/thumbnail_video.png !public/media/thumbnail_other.png diff --git a/docker-compose.server.yml b/docker-compose.server.yml index 614d67b10..bc011cac0 100644 --- a/docker-compose.server.yml +++ b/docker-compose.server.yml @@ -8,7 +8,7 @@ networks: services: phpfpm: - image: itkdev/php8.3-fpm:alpine + image: itkdev/php8.4-fpm:alpine restart: unless-stopped networks: - app diff --git a/docker-compose.yml b/docker-compose.yml index b5c08946d..b18ab3814 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -27,7 +27,7 @@ services: #- ENCRYPT=1 # Uncomment to enable database encryption. phpfpm: - image: itkdev/php8.3-fpm:latest + image: itkdev/php8.4-fpm:latest user: ${COMPOSE_USER:-deploy} networks: - app diff --git a/infrastructure/build-n-push.sh b/infrastructure/build-n-push.sh new file mode 100755 index 000000000..04f244043 --- /dev/null +++ b/infrastructure/build-n-push.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +set -eux + +APP_VERSION=develop + +docker pull itkdev/php8.4-fpm:alpine +docker pull nginxinc/nginx-unprivileged:alpine + +docker build --build-context repository-root=.. \ + --platform linux/amd64,linux/arm64 \ + --pull \ + --no-cache \ + --build-arg APP_VERSION=${APP_VERSION} \ + --tag=turegjorup/display-api-service:${APP_VERSION} \ + --file="display-api-service/Dockerfile" display-api-service + + +docker build --build-context repository-root=.. \ + --platform linux/amd64,linux/arm64 \ + --no-cache \ + --build-arg VERSION=${APP_VERSION} \ + --tag=turegjorup/display-api-service-nginx:${APP_VERSION} \ + --file="nginx/Dockerfile" nginx + +docker push os2display/display-api-service:${APP_VERSION} +docker push os2display/display-api-service-nginx:${APP_VERSION} diff --git a/infrastructure/display-api-service/Dockerfile b/infrastructure/display-api-service/Dockerfile index 5e9c835dd..f50de55ff 100644 --- a/infrastructure/display-api-service/Dockerfile +++ b/infrastructure/display-api-service/Dockerfile @@ -1,33 +1,43 @@ -FROM itkdev/php8.4-fpm:alpine AS APP_BUILDER +######### API backend ######## +FROM itkdev/php8.4-fpm:alpine AS api_app_builder LABEL maintainer="ITK Dev " ARG APP_VERSION="develop" -ENV APP_PATH=/var/www/html +ENV APP_API_PATH=/var/www/html USER root -# Move site into the container. -ADD https://github.com/os2display/display-api-service/archive/${APP_VERSION}.tar.gz /tmp/app.tar -RUN tar -zxf /tmp/app.tar --strip-components=1 -C ${APP_PATH} \ - && rm /tmp/app.tar - # Add composer in from the official composer image (also alpine). COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer -WORKDIR ${APP_PATH} +WORKDIR ${APP_API_PATH} + +USER deploy + +# Copy only composer files first for better layer caching +COPY --chown=deploy:deploy --from=repository-root composer.json composer.lock symfony.lock ${APP_API_PATH}/ + +# Install composer packages first (better image layer caching) +RUN APP_ENV=prod composer install --no-dev -o --classmap-authoritative --no-scripts + +# Copy application source (needed for build step) +COPY --chown=deploy:deploy --from=repository-root ./ ${APP_API_PATH}/ + +# Remove frontend apps from api build +RUN rm -rf /assets -## Install assets, which requires a HACK as redis is not available (should be removed later on). -RUN COMPOSER_ALLOW_SUPERUSER=1 APP_ENV=prod composer install --no-dev -o --classmap-authoritative +# Install composer packages, which requires a HACK as redis is not available (should be removed later on). +RUN APP_ENV=prod composer install --no-dev -o --classmap-authoritative -RUN rm -rf infrastructure #### ## Build main application image. #### -FROM itkdev/php8.3-fpm:alpine + +FROM itkdev/php8.4-fpm:alpine LABEL maintainer="ITK Dev " -ENV APP_PATH=/var/www/html \ +ENV APP_API_PATH=/var/www/html \ COMPOSER_VERSION=2 USER root @@ -35,29 +45,18 @@ USER root # Add composer needed to run optimizations after config is loaded. COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer -# Install the application. -COPY --from=APP_BUILDER ${APP_PATH} ${APP_PATH} -RUN mkdir -p ${APP_PATH}/config/secrets \ - && chown -R deploy:deploy ${APP_PATH} - # Download Prometheus php-fpm export. -COPY --from=hipages/php-fpm_exporter:1.1.1 /php-fpm_exporter /usr/local/bin/php-fpm_exporter - -# Copy configuration. -COPY etc /etc/ - -# Install configuration template handler -ADD https://github.com/kelseyhightower/confd/releases/download/v0.16.0/confd-0.16.0-linux-amd64 /usr/local/bin/confd -RUN chmod +x /usr/local/bin/confd - -# Copy confd onfiguration. -COPY etc /etc/ +COPY --from=hipages/php-fpm_exporter:2.2.0 /php-fpm_exporter /usr/local/bin/php-fpm_exporter COPY docker-entrypoint.sh /usr/local/bin/ RUN chmod +x /usr/local/bin/docker-entrypoint.sh USER deploy -WORKDIR ${APP_PATH} +# Install the api application. +COPY --chown=deploy:deploy --from=api_app_builder ${APP_API_PATH} ${APP_API_PATH} +RUN mkdir -p ${APP_API_PATH}/config/secrets + +WORKDIR ${APP_API_PATH} CMD [ "docker-entrypoint.sh" ] diff --git a/infrastructure/display-api-service/docker-entrypoint.sh b/infrastructure/display-api-service/docker-entrypoint.sh index 306e9f13d..744897e8b 100644 --- a/infrastructure/display-api-service/docker-entrypoint.sh +++ b/infrastructure/display-api-service/docker-entrypoint.sh @@ -2,10 +2,8 @@ set -eux -## Run templates with configuration. -/usr/local/bin/confd --onetime --backend env --confdir /etc/confd - -## Bump env.local into PHP for better performance. +## Dump dotenv files into PHP for better performance. +## @see https://symfony.com/doc/6.4/configuration.html#configuring-environment-variables-in-production composer dump-env prod ## Warm-up Symfony cache (with the current configuration). diff --git a/infrastructure/display-api-service/etc/confd/conf.d/env.local.toml b/infrastructure/display-api-service/etc/confd/conf.d/env.local.toml deleted file mode 100644 index d614cc69a..000000000 --- a/infrastructure/display-api-service/etc/confd/conf.d/env.local.toml +++ /dev/null @@ -1,8 +0,0 @@ -[template] -src = "env.local.tmpl" -dest = "/var/www/html/.env.local" -owner = "deploy" -mode = "0644" -keys = [ - "/app-config" -] diff --git a/infrastructure/display-api-service/etc/confd/templates/env.local.tmpl b/infrastructure/display-api-service/etc/confd/templates/env.local.tmpl deleted file mode 100644 index 76e585b97..000000000 --- a/infrastructure/display-api-service/etc/confd/templates/env.local.tmpl +++ /dev/null @@ -1,71 +0,0 @@ -###> symfony/framework-bundle ### -APP_ENV={{ getenv "APP_ENV" "prod" }} -APP_SECRET={{ getenv "APP_SECRET" "MySuperSecret" }} -TRUSTED_PROXIES={{ getenv "APP_TRUSTED_PROXIES" "127.0.0.1,REMOTE_ADDR" }} -###< symfony/framework-bundle ### - -###> doctrine/doctrine-bundle ### -DATABASE_URL={{ getenv "APP_DATABASE_URL" "mysql://db:db@mariadb:3306/db?serverVersion=mariadb-10.4.0" }} -###< doctrine/doctrine-bundle ### - -###> nelmio/cors-bundle ### -CORS_ALLOW_ORIGIN={{ getenv "APP_CORS_ALLOW_ORIGIN" "'^https?://localhost(:[0-9]+)?$'" }} -###< nelmio/cors-bundle ### - -###> App ### -APP_DEFAULT_DATE_FORMAT='{{ getenv "APP_DEFAULT_DATE_FORMAT" "Y-m-d\\TH:i:s\\Z" }}' -APP_ACTIVATION_CODE_EXPIRE_INTERVAL='{{ getenv "APP_ACTIVATION_CODE_EXPIRE_INTERVAL" "P2D" }}' -###< App ### - -###> lexik/jwt-authentication-bundle ### -JWT_PASSPHRASE={{ getenv "APP_JWT_PASSPHRASE" }} -JWT_TOKEN_TTL={{ getenv "APP_JWT_TOKEN_TTL" "3600" }} -JWT_SCREEN_TOKEN_TTL={{ getenv "APP_JWT_SCREEN_TOKEN_TTL" "3600" }} -###< lexik/jwt-authentication-bundle ### - -###> gesdinet/jwt-refresh-token-bundle ### -JWT_REFRESH_TOKEN_TTL={{ getenv "APP_JWT_REFRESH_TOKEN_TTL" "2592000" }} -JWT_SCREEN_REFRESH_TOKEN_TTL={{ getenv "APP_JWT_SCREEN_REFRESH_TOKEN_TTL" "2592000" }} -###< gesdinet/jwt-refresh-token-bundle ### - -###> itk-dev/openid-connect-bundle ### -# internal provider -INTERNAL_OIDC_METADATA_URL={{ getenv "APP_INTERNAL_OIDC_METADATA_URL" "" }} -INTERNAL_OIDC_CLIENT_ID={{ getenv "APP_INTERNAL_OIDC_CLIENT_ID" "" }} -INTERNAL_OIDC_CLIENT_SECRET={{ getenv "APP_INTERNAL_OIDC_CLIENT_SECRET" "" }} -INTERNAL_OIDC_REDIRECT_URI={{ getenv "APP_INTERNAL_OIDC_REDIRECT_URI" "" }} -INTERNAL_OIDC_LEEWAY={{ getenv "APP_INTERNAL_OIDC_LEEWAY" "30" }} - -# external provider -EXTERNAL_OIDC_METADATA_URL={{ getenv "APP_EXTERNAL_OIDC_METADATA_URL" "" }} -EXTERNAL_OIDC_CLIENT_ID={{ getenv "APP_EXTERNAL_OIDC_CLIENT_ID" "" }} -EXTERNAL_OIDC_CLIENT_SECRET={{ getenv "APP_EXTERNAL_OIDC_CLIENT_SECRET" "" }} -EXTERNAL_OIDC_REDIRECT_URI={{ getenv "APP_EXTERNAL_OIDC_REDIRECT_URI" "" }} -EXTERNAL_OIDC_LEEWAY={{ getenv "APP_EXTERNAL_OIDC_LEEWAY" "30" }} -EXTERNAL_OIDC_HASH_SALT={{ getenv "APP_EXTERNAL_OIDC_HASH_SALT" "" }} - -CLI_REDIRECT={{ getenv "APP_CLI_REDIRECT" "" }} -###< itk-dev/openid-connect-bundle ### - -###> redis ### -REDIS_CACHE_PREFIX={{ getenv "APP_CLI_REDIRECT" "DisplayApiService" }} -REDIS_CACHE_DSN={{ getenv "APP_CLI_REDIRECT" "redis://redis:6379/0" }} -###< redis ### - -###> Calendar Api Feed Source ### -CALENDAR_API_FEED_SOURCE_LOCATION_ENDPOINT={{ getenv "APP_CALENDAR_API_FEED_SOURCE_LOCATION_ENDPOINT" "" }} -CALENDAR_API_FEED_SOURCE_RESOURCE_ENDPOINT={{ getenv "APP_CALENDAR_API_FEED_SOURCE_RESOURCE_ENDPOINT" "" }} -CALENDAR_API_FEED_SOURCE_EVENT_ENDPOINT={{ getenv "APP_CALENDAR_API_FEED_SOURCE_EVENT_ENDPOINT" "" }} -CALENDAR_API_FEED_SOURCE_CUSTOM_MAPPINGS={{ getenv "APP_CALENDAR_API_FEED_SOURCE_CUSTOM_MAPPINGS" "'{}'" }} -CALENDAR_API_FEED_SOURCE_EVENT_MODIFIERS={{ getenv "APP_CALENDAR_API_FEED_SOURCE_EVENT_MODIFIERS" "'{}'" }} -CALENDAR_API_FEED_SOURCE_DATE_FORMAT={{ getenv "APP_CALENDAR_API_FEED_SOURCE_DATE_FORMAT" "" }} -CALENDAR_API_FEED_SOURCE_DATE_TIMEZONE={{ getenv "APP_CALENDAR_API_FEED_SOURCE_DATE_TIMEZONE" "" }} -CALENDAR_API_FEED_SOURCE_CACHE_EXPIRE_SECONDS={{ getenv "APP_CALENDAR_API_FEED_SOURCE_CACHE_EXPIRE_SECONDS" "300" }} -###< Calendar Api Feed Source ### - -EVENTDATABASE_API_V2_CACHE_EXPIRE_SECONDS={{ getenv "APP_EVENTDATABASE_API_V2_CACHE_EXPIRE_SECONDS" "300" }} - -TRACK_SCREEN_INFO={{ getenv "APP_TRACK_SCREEN_INFO" "false" }} -TRACK_SCREEN_INFO_UPDATE_INTERVAL_SECONDS={{ getenv "APP_TRACK_SCREEN_INFO_UPDATE_INTERVAL_SECONDS" "300" }} - -APP_KEY_VAULT_JSON={{ getenv "APP_KEY_VAULT_JSON" "{}" }} diff --git a/infrastructure/nginx/Dockerfile b/infrastructure/nginx/Dockerfile index 6be4be3c4..6754aba58 100644 --- a/infrastructure/nginx/Dockerfile +++ b/infrastructure/nginx/Dockerfile @@ -1,20 +1,49 @@ +######## Client [Screen|Admin] build ######## +FROM node:24-alpine AS client_app_builder +LABEL maintainer="ITK Dev " + ARG APP_VERSION="develop" -FROM itkdev/os2display-api-service:${APP_VERSION} as APPLICATION +ARG APP_RELEASE_VERSION="develop" +ARG APP_RELEASE_TIMESTAMP=0 +ENV APP_CLIENT_PATH=/app + +WORKDIR ${APP_CLIENT_PATH} + +# Copy only necessary files for npm install +COPY --from=repository-root package.json package-lock.json vite.config.js ${APP_CLIENT_PATH}/ + +# Install dependencies +RUN npm ci --no-audit --no-fund +# Copy source files needed for build +COPY --from=repository-root assets/ ${APP_CLIENT_PATH}/assets/ +COPY --from=repository-root public/client/ ${APP_CLIENT_PATH}/public/client/ +COPY --from=repository-root vite.config.js ${APP_CLIENT_PATH}/ + +# Build the application +RUN npm run build + +# Clean up build dependencies and cache +RUN npm cache clean --force && \ + rm -rf node_modules/.cache && \ + rm -rf /root/.npm + + +######## Nginx build ######## FROM nginxinc/nginx-unprivileged:alpine LABEL maintainer="ITK Dev " ARG UID=101 ARG GID=101 -ENV APP_PATH=/var/www/html +ENV APP_CLIENT_PATH=/app +ENV APP_API_PATH=/var/www/html USER root -RUN mkdir -p ${APP_PATH}/public - -COPY --from=APPLICATION ${APP_PATH}/public ${APP_PATH}/public +RUN mkdir -p ${APP_API_PATH}/public +COPY --from=client_app_builder ${APP_CLIENT_PATH}/public ${APP_API_PATH}/public -WORKDIR ${APP_PATH} +WORKDIR ${APP_API_PATH} # Copy configuration. COPY etc /etc/ @@ -25,8 +54,8 @@ RUN chmod +x /usr/local/bin/confd COPY docker-entrypoint.sh / RUN chmod +x /docker-entrypoint.sh \ - && chown -R $UID:0 ${APP_PATH} \ - && chmod -R g+w ${APP_PATH} + && chown -R $UID:0 ${APP_API_PATH} \ + && chmod -R g+w ${APP_API_PATH} USER $UID diff --git a/infrastructure/nginx/etc/confd/templates/default.conf.tmpl b/infrastructure/nginx/etc/confd/templates/default.conf.tmpl index b6f32099b..7fcd10283 100644 --- a/infrastructure/nginx/etc/confd/templates/default.conf.tmpl +++ b/infrastructure/nginx/etc/confd/templates/default.conf.tmpl @@ -11,6 +11,29 @@ server { try_files $uri /index.php$is_args$args; } + # Cache static assets built by Vite under /public/build + location /build/ { + access_log off; + log_not_found off; + + # Serve files directly + try_files $uri =404; + + # Long-term caching for fingerprinted assets + add_header Cache-Control "public, max-age=31536000, immutable"; + expires 1y; + + # Also allow CORS for fonts and similar assets if needed + add_header Access-Control-Allow-Origin "*" always; + + # Do not set X-Robots-Tag for assets + } + + # Screen client online check should just serve static files + location /client/online-check { + index index.html index.htm; + } + location = /robots.txt { add_header Content-Type text/plain; add_header X-Robots-Tag "noindex, nofollow, nosnippet, noarchive"; diff --git a/infrastructure/run.sh b/infrastructure/run.sh deleted file mode 100755 index b028f5cdc..000000000 --- a/infrastructure/run.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh - -set -eux - -APP_VERSION=develop - -docker pull nginxinc/nginx-unprivileged:alpine - -docker build --pull --no-cache --build-arg APP_VERSION=${APP_VERSION} --tag=itkdev/os2display-api-service:${APP_VERSION} --file="display-api-service/Dockerfile" display-api-service -docker build --no-cache --build-arg VERSION=${APP_VERSION} --tag=itkdev/os2display-api-service-nginx:${APP_VERSION} --file="nginx/Dockerfile" nginx - -# docker push itkdev/os2display-api-service:${APP_VERSION} -# docker push itkdev/os2display-api-service-nginx:${APP_VERSION} From 7628dc0925b6534f315ea743e99dd2458f76945e Mon Sep 17 00:00:00 2001 From: turegjorup Date: Mon, 8 Dec 2025 08:47:11 +0100 Subject: [PATCH 05/59] 5402: Fix Vite missing prod caching config --- config/packages/pentatrion_vite.yaml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 config/packages/pentatrion_vite.yaml diff --git a/config/packages/pentatrion_vite.yaml b/config/packages/pentatrion_vite.yaml new file mode 100644 index 000000000..c8b11e194 --- /dev/null +++ b/config/packages/pentatrion_vite.yaml @@ -0,0 +1,4 @@ +# @see https://symfony-vite.pentatrion.com/guide/performance.html#caching-configuration-files-%F0%9F%8F%83 +when@prod: + pentatrion_vite: + cache: true From acc6ea89feeae8ac0f91203ad7a17677aeace3f1 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Mon, 8 Dec 2025 08:48:17 +0100 Subject: [PATCH 06/59] 5402: Update Psalm to enable PHP 8.4 image builds --- composer.json | 2 +- composer.lock | 3591 +++++++++++++++++++++++++++++++-------------- package-lock.json | 24 +- 3 files changed, 2478 insertions(+), 1139 deletions(-) diff --git a/composer.json b/composer.json index 3108dd75c..30132d0cb 100644 --- a/composer.json +++ b/composer.json @@ -63,7 +63,7 @@ "symfony/stopwatch": "~6.4.0", "symfony/var-dumper": "~6.4.0", "symfony/web-profiler-bundle": "~6.4.0", - "vimeo/psalm": "^5.12.0", + "vimeo/psalm": "^6.13.0", "vincentlanglet/twig-cs-fixer": "^3.8", "weirdan/doctrine-psalm-plugin": "^2.0" }, diff --git a/composer.lock b/composer.lock index edaae5488..37f155558 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "1bd6fd456477d1e03a03bb4359ad3e77", + "content-hash": "567a746ddcb5a440c396fc6ea9cfa7ac", "packages": [ { "name": "api-platform/core", @@ -445,16 +445,16 @@ }, { "name": "composer/semver", - "version": "3.4.3", + "version": "3.4.4", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12" + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", - "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", "shasum": "" }, "require": { @@ -506,7 +506,7 @@ "support": { "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.4.3" + "source": "https://github.com/composer/semver/tree/3.4.4" }, "funding": [ { @@ -516,13 +516,9 @@ { "url": "https://github.com/composer", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" } ], - "time": "2024-09-19T14:15:21+00:00" + "time": "2025-08-20T19:15:30+00:00" }, { "name": "composer/xdebug-handler", @@ -660,6 +656,7 @@ "issues": "https://github.com/alexdebril/feed-io/issues", "source": "https://github.com/alexdebril/feed-io/tree/v6.0.3" }, + "abandoned": "php-feed-io/feed-io", "time": "2023-10-16T14:12:49+00:00" }, { @@ -753,20 +750,21 @@ "type": "tidelift" } ], + "abandoned": true, "time": "2022-05-20T20:07:39+00:00" }, { "name": "doctrine/collections", - "version": "2.3.0", + "version": "2.4.0", "source": { "type": "git", "url": "https://github.com/doctrine/collections.git", - "reference": "2eb07e5953eed811ce1b309a7478a3b236f2273d" + "reference": "9acfeea2e8666536edff3d77c531261c63680160" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/collections/zipball/2eb07e5953eed811ce1b309a7478a3b236f2273d", - "reference": "2eb07e5953eed811ce1b309a7478a3b236f2273d", + "url": "https://api.github.com/repos/doctrine/collections/zipball/9acfeea2e8666536edff3d77c531261c63680160", + "reference": "9acfeea2e8666536edff3d77c531261c63680160", "shasum": "" }, "require": { @@ -775,11 +773,11 @@ "symfony/polyfill-php84": "^1.30" }, "require-dev": { - "doctrine/coding-standard": "^12", + "doctrine/coding-standard": "^14", "ext-json": "*", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^10.5" + "phpstan/phpstan": "^2.1.30", + "phpstan/phpstan-phpunit": "^2.0.7", + "phpunit/phpunit": "^10.5.58 || ^11.5.42 || ^12.4" }, "type": "library", "autoload": { @@ -823,7 +821,7 @@ ], "support": { "issues": "https://github.com/doctrine/collections/issues", - "source": "https://github.com/doctrine/collections/tree/2.3.0" + "source": "https://github.com/doctrine/collections/tree/2.4.0" }, "funding": [ { @@ -839,7 +837,7 @@ "type": "tidelift" } ], - "time": "2025-03-22T10:17:19+00:00" + "time": "2025-10-25T09:18:13+00:00" }, { "name": "doctrine/common", @@ -934,36 +932,39 @@ }, { "name": "doctrine/dbal", - "version": "3.9.5", + "version": "3.10.3", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "4a4e2eed3134036ee36a147ee0dac037dfa17868" + "reference": "65edaca19a752730f290ec2fb89d593cb40afb43" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/4a4e2eed3134036ee36a147ee0dac037dfa17868", - "reference": "4a4e2eed3134036ee36a147ee0dac037dfa17868", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/65edaca19a752730f290ec2fb89d593cb40afb43", + "reference": "65edaca19a752730f290ec2fb89d593cb40afb43", "shasum": "" }, "require": { "composer-runtime-api": "^2", - "doctrine/cache": "^1.11|^2.0", "doctrine/deprecations": "^0.5.3|^1", "doctrine/event-manager": "^1|^2", "php": "^7.4 || ^8.0", "psr/cache": "^1|^2|^3", "psr/log": "^1|^2|^3" }, + "conflict": { + "doctrine/cache": "< 1.11" + }, "require-dev": { - "doctrine/coding-standard": "13.0.0", + "doctrine/cache": "^1.11|^2.0", + "doctrine/coding-standard": "14.0.0", "fig/log-test": "^1", "jetbrains/phpstorm-stubs": "2023.1", - "phpstan/phpstan": "2.1.17", + "phpstan/phpstan": "2.1.30", "phpstan/phpstan-strict-rules": "^2", - "phpunit/phpunit": "9.6.23", - "slevomat/coding-standard": "8.16.2", - "squizlabs/php_codesniffer": "3.13.1", + "phpunit/phpunit": "9.6.29", + "slevomat/coding-standard": "8.24.0", + "squizlabs/php_codesniffer": "4.0.0", "symfony/cache": "^5.4|^6.0|^7.0", "symfony/console": "^4.4|^5.4|^6.0|^7.0" }, @@ -1025,7 +1026,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.9.5" + "source": "https://github.com/doctrine/dbal/tree/3.10.3" }, "funding": [ { @@ -1041,7 +1042,7 @@ "type": "tidelift" } ], - "time": "2025-06-15T22:40:05+00:00" + "time": "2025-10-09T09:05:12+00:00" }, { "name": "doctrine/deprecations", @@ -1093,20 +1094,21 @@ }, { "name": "doctrine/doctrine-bundle", - "version": "2.15.0", + "version": "2.18.1", "source": { "type": "git", "url": "https://github.com/doctrine/DoctrineBundle.git", - "reference": "d88294521a1bca943240adca65fa19ca8a7288c6" + "reference": "b769877014de053da0e5cbbb63d0ea2f3b2fea76" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/d88294521a1bca943240adca65fa19ca8a7288c6", - "reference": "d88294521a1bca943240adca65fa19ca8a7288c6", + "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/b769877014de053da0e5cbbb63d0ea2f3b2fea76", + "reference": "b769877014de053da0e5cbbb63d0ea2f3b2fea76", "shasum": "" }, "require": { "doctrine/dbal": "^3.7.0 || ^4.0", + "doctrine/deprecations": "^1.0", "doctrine/persistence": "^3.1 || ^4", "doctrine/sql-formatter": "^1.0.1", "php": "^8.1", @@ -1114,7 +1116,6 @@ "symfony/config": "^6.4 || ^7.0", "symfony/console": "^6.4 || ^7.0", "symfony/dependency-injection": "^6.4 || ^7.0", - "symfony/deprecation-contracts": "^2.1 || ^3", "symfony/doctrine-bridge": "^6.4.3 || ^7.0.3", "symfony/framework-bundle": "^6.4 || ^7.0", "symfony/service-contracts": "^2.5 || ^3" @@ -1129,18 +1130,17 @@ "require-dev": { "doctrine/annotations": "^1 || ^2", "doctrine/cache": "^1.11 || ^2.0", - "doctrine/coding-standard": "^13", - "doctrine/deprecations": "^1.0", + "doctrine/coding-standard": "^14", "doctrine/orm": "^2.17 || ^3.1", "friendsofphp/proxy-manager-lts": "^1.0", "phpstan/phpstan": "2.1.1", "phpstan/phpstan-phpunit": "2.0.3", "phpstan/phpstan-strict-rules": "^2", - "phpunit/phpunit": "^9.6.22", + "phpunit/phpunit": "^10.5.53 || ^12.3.10", "psr/log": "^1.1.4 || ^2.0 || ^3.0", "symfony/doctrine-messenger": "^6.4 || ^7.0", + "symfony/expression-language": "^6.4 || ^7.0", "symfony/messenger": "^6.4 || ^7.0", - "symfony/phpunit-bridge": "^7.2", "symfony/property-info": "^6.4 || ^7.0", "symfony/security-bundle": "^6.4 || ^7.0", "symfony/stopwatch": "^6.4 || ^7.0", @@ -1150,7 +1150,7 @@ "symfony/var-exporter": "^6.4.1 || ^7.0.1", "symfony/web-profiler-bundle": "^6.4 || ^7.0", "symfony/yaml": "^6.4 || ^7.0", - "twig/twig": "^2.13 || ^3.0.4" + "twig/twig": "^2.14.7 || ^3.0.4" }, "suggest": { "doctrine/orm": "The Doctrine ORM integration is optional in the bundle.", @@ -1195,7 +1195,7 @@ ], "support": { "issues": "https://github.com/doctrine/DoctrineBundle/issues", - "source": "https://github.com/doctrine/DoctrineBundle/tree/2.15.0" + "source": "https://github.com/doctrine/DoctrineBundle/tree/2.18.1" }, "funding": [ { @@ -1211,32 +1211,32 @@ "type": "tidelift" } ], - "time": "2025-06-16T19:53:58+00:00" + "time": "2025-11-05T14:42:10+00:00" }, { "name": "doctrine/doctrine-migrations-bundle", - "version": "3.4.2", + "version": "3.7.0", "source": { "type": "git", "url": "https://github.com/doctrine/DoctrineMigrationsBundle.git", - "reference": "5a6ac7120c2924c4c070a869d08b11ccf9e277b9" + "reference": "1e380c6dd8ac8488217f39cff6b77e367f1a644b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/DoctrineMigrationsBundle/zipball/5a6ac7120c2924c4c070a869d08b11ccf9e277b9", - "reference": "5a6ac7120c2924c4c070a869d08b11ccf9e277b9", + "url": "https://api.github.com/repos/doctrine/DoctrineMigrationsBundle/zipball/1e380c6dd8ac8488217f39cff6b77e367f1a644b", + "reference": "1e380c6dd8ac8488217f39cff6b77e367f1a644b", "shasum": "" }, "require": { - "doctrine/doctrine-bundle": "^2.4", + "doctrine/doctrine-bundle": "^2.4 || ^3.0", "doctrine/migrations": "^3.2", "php": "^7.2 || ^8.0", "symfony/deprecation-contracts": "^2.1 || ^3", - "symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0" + "symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0 || ^8.0" }, "require-dev": { "composer/semver": "^3.0", - "doctrine/coding-standard": "^12", + "doctrine/coding-standard": "^12 || ^14", "doctrine/orm": "^2.6 || ^3", "phpstan/phpstan": "^1.4 || ^2", "phpstan/phpstan-deprecation-rules": "^1 || ^2", @@ -1244,8 +1244,8 @@ "phpstan/phpstan-strict-rules": "^1.1 || ^2", "phpstan/phpstan-symfony": "^1.3 || ^2", "phpunit/phpunit": "^8.5 || ^9.5", - "symfony/phpunit-bridge": "^6.3 || ^7", - "symfony/var-exporter": "^5.4 || ^6 || ^7" + "symfony/phpunit-bridge": "^6.3 || ^7 || ^8", + "symfony/var-exporter": "^5.4 || ^6 || ^7 || ^8" }, "type": "symfony-bundle", "autoload": { @@ -1280,7 +1280,7 @@ ], "support": { "issues": "https://github.com/doctrine/DoctrineMigrationsBundle/issues", - "source": "https://github.com/doctrine/DoctrineMigrationsBundle/tree/3.4.2" + "source": "https://github.com/doctrine/DoctrineMigrationsBundle/tree/3.7.0" }, "funding": [ { @@ -1296,7 +1296,7 @@ "type": "tidelift" } ], - "time": "2025-03-11T17:36:26+00:00" + "time": "2025-11-15T19:02:59+00:00" }, { "name": "doctrine/event-manager", @@ -1391,33 +1391,32 @@ }, { "name": "doctrine/inflector", - "version": "2.0.10", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/inflector.git", - "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc" + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/5817d0659c5b50c9b950feb9af7b9668e2c436bc", - "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/6d6c96277ea252fc1304627204c3d5e6e15faa3b", + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^11.0", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.3", - "phpunit/phpunit": "^8.5 || ^9.5", - "vimeo/psalm": "^4.25 || ^5.4" + "doctrine/coding-standard": "^12.0 || ^13.0", + "phpstan/phpstan": "^1.12 || ^2.0", + "phpstan/phpstan-phpunit": "^1.4 || ^2.0", + "phpstan/phpstan-strict-rules": "^1.6 || ^2.0", + "phpunit/phpunit": "^8.5 || ^12.2" }, "type": "library", "autoload": { "psr-4": { - "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" + "Doctrine\\Inflector\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1462,7 +1461,7 @@ ], "support": { "issues": "https://github.com/doctrine/inflector/issues", - "source": "https://github.com/doctrine/inflector/tree/2.0.10" + "source": "https://github.com/doctrine/inflector/tree/2.1.0" }, "funding": [ { @@ -1478,7 +1477,7 @@ "type": "tidelift" } ], - "time": "2024-02-18T20:23:39+00:00" + "time": "2025-08-10T19:31:58+00:00" }, { "name": "doctrine/instantiator", @@ -1629,16 +1628,16 @@ }, { "name": "doctrine/migrations", - "version": "3.9.1", + "version": "3.9.5", "source": { "type": "git", "url": "https://github.com/doctrine/migrations.git", - "reference": "0f1e0c960ac29866d648a4f50142a74fe1cb6999" + "reference": "1b823afbc40f932dae8272574faee53f2755eac5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/migrations/zipball/0f1e0c960ac29866d648a4f50142a74fe1cb6999", - "reference": "0f1e0c960ac29866d648a4f50142a74fe1cb6999", + "url": "https://api.github.com/repos/doctrine/migrations/zipball/1b823afbc40f932dae8272574faee53f2755eac5", + "reference": "1b823afbc40f932dae8272574faee53f2755eac5", "shasum": "" }, "require": { @@ -1648,29 +1647,29 @@ "doctrine/event-manager": "^1.2 || ^2.0", "php": "^8.1", "psr/log": "^1.1.3 || ^2 || ^3", - "symfony/console": "^5.4 || ^6.0 || ^7.0", - "symfony/stopwatch": "^5.4 || ^6.0 || ^7.0", - "symfony/var-exporter": "^6.2 || ^7.0" + "symfony/console": "^5.4 || ^6.0 || ^7.0 || ^8.0", + "symfony/stopwatch": "^5.4 || ^6.0 || ^7.0 || ^8.0", + "symfony/var-exporter": "^6.2 || ^7.0 || ^8.0" }, "conflict": { "doctrine/orm": "<2.12 || >=4" }, "require-dev": { - "doctrine/coding-standard": "^12", + "doctrine/coding-standard": "^14", "doctrine/orm": "^2.13 || ^3", "doctrine/persistence": "^2 || ^3 || ^4", "doctrine/sql-formatter": "^1.0", "ext-pdo_sqlite": "*", "fig/log-test": "^1", - "phpstan/phpstan": "^1.10", - "phpstan/phpstan-deprecation-rules": "^1.1", - "phpstan/phpstan-phpunit": "^1.3", - "phpstan/phpstan-strict-rules": "^1.4", - "phpstan/phpstan-symfony": "^1.3", - "phpunit/phpunit": "^10.3", - "symfony/cache": "^5.4 || ^6.0 || ^7.0", - "symfony/process": "^5.4 || ^6.0 || ^7.0", - "symfony/yaml": "^5.4 || ^6.0 || ^7.0" + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-phpunit": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpstan/phpstan-symfony": "^2", + "phpunit/phpunit": "^10.3 || ^11.0 || ^12.0", + "symfony/cache": "^5.4 || ^6.0 || ^7.0 || ^8.0", + "symfony/process": "^5.4 || ^6.0 || ^7.0 || ^8.0", + "symfony/yaml": "^5.4 || ^6.0 || ^7.0 || ^8.0" }, "suggest": { "doctrine/sql-formatter": "Allows to generate formatted SQL with the diff command.", @@ -1712,7 +1711,7 @@ ], "support": { "issues": "https://github.com/doctrine/migrations/issues", - "source": "https://github.com/doctrine/migrations/tree/3.9.1" + "source": "https://github.com/doctrine/migrations/tree/3.9.5" }, "funding": [ { @@ -1728,20 +1727,20 @@ "type": "tidelift" } ], - "time": "2025-06-27T07:19:23+00:00" + "time": "2025-11-20T11:15:36+00:00" }, { "name": "doctrine/orm", - "version": "2.20.5", + "version": "2.20.8", "source": { "type": "git", "url": "https://github.com/doctrine/orm.git", - "reference": "6307b4fa7d7e3845a756106977e3b48907622098" + "reference": "5bff0919a78c86238536a9b5396024fe3603b5d1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/orm/zipball/6307b4fa7d7e3845a756106977e3b48907622098", - "reference": "6307b4fa7d7e3845a756106977e3b48907622098", + "url": "https://api.github.com/repos/doctrine/orm/zipball/5bff0919a78c86238536a9b5396024fe3603b5d1", + "reference": "5bff0919a78c86238536a9b5396024fe3603b5d1", "shasum": "" }, "require": { @@ -1768,14 +1767,13 @@ }, "require-dev": { "doctrine/annotations": "^1.13 || ^2", - "doctrine/coding-standard": "^9.0.2 || ^13.0", + "doctrine/coding-standard": "^9.0.2 || ^14.0", "phpbench/phpbench": "^0.16.10 || ^1.0", "phpstan/extension-installer": "~1.1.0 || ^1.4", - "phpstan/phpstan": "~1.4.10 || 2.0.3", + "phpstan/phpstan": "~1.4.10 || 2.1.23", "phpstan/phpstan-deprecation-rules": "^1 || ^2", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6", "psr/log": "^1 || ^2 || ^3", - "squizlabs/php_codesniffer": "3.12.0", "symfony/cache": "^4.4 || ^5.4 || ^6.4 || ^7.0", "symfony/var-exporter": "^4.4 || ^5.4 || ^6.2 || ^7.0", "symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0 || ^7.0" @@ -1828,22 +1826,22 @@ ], "support": { "issues": "https://github.com/doctrine/orm/issues", - "source": "https://github.com/doctrine/orm/tree/2.20.5" + "source": "https://github.com/doctrine/orm/tree/2.20.8" }, - "time": "2025-06-24T17:50:46+00:00" + "time": "2025-11-10T13:35:45+00:00" }, { "name": "doctrine/persistence", - "version": "3.4.0", + "version": "3.4.3", "source": { "type": "git", "url": "https://github.com/doctrine/persistence.git", - "reference": "0ea965320cec355dba75031c1b23d4c78362e3ff" + "reference": "d59e6ef7caffe6a30f4b6f9e9079a75f52c64ae0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/persistence/zipball/0ea965320cec355dba75031c1b23d4c78362e3ff", - "reference": "0ea965320cec355dba75031c1b23d4c78362e3ff", + "url": "https://api.github.com/repos/doctrine/persistence/zipball/d59e6ef7caffe6a30f4b6f9e9079a75f52c64ae0", + "reference": "d59e6ef7caffe6a30f4b6f9e9079a75f52c64ae0", "shasum": "" }, "require": { @@ -1855,11 +1853,11 @@ "doctrine/common": "<2.10" }, "require-dev": { - "doctrine/coding-standard": "^12", + "doctrine/coding-standard": "^12 || ^14", "doctrine/common": "^3.0", - "phpstan/phpstan": "1.12.7", - "phpstan/phpstan-phpunit": "^1", - "phpstan/phpstan-strict-rules": "^1.1", + "phpstan/phpstan": "^1 || 2.1.30", + "phpstan/phpstan-phpunit": "^1 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2", "phpunit/phpunit": "^8.5.38 || ^9.5", "symfony/cache": "^4.4 || ^5.4 || ^6.0 || ^7.0" }, @@ -1910,7 +1908,7 @@ ], "support": { "issues": "https://github.com/doctrine/persistence/issues", - "source": "https://github.com/doctrine/persistence/tree/3.4.0" + "source": "https://github.com/doctrine/persistence/tree/3.4.3" }, "funding": [ { @@ -1926,30 +1924,30 @@ "type": "tidelift" } ], - "time": "2024-10-30T19:48:12+00:00" + "time": "2025-10-21T15:21:39+00:00" }, { "name": "doctrine/sql-formatter", - "version": "1.5.2", + "version": "1.5.3", "source": { "type": "git", "url": "https://github.com/doctrine/sql-formatter.git", - "reference": "d6d00aba6fd2957fe5216fe2b7673e9985db20c8" + "reference": "a8af23a8e9d622505baa2997465782cbe8bb7fc7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/sql-formatter/zipball/d6d00aba6fd2957fe5216fe2b7673e9985db20c8", - "reference": "d6d00aba6fd2957fe5216fe2b7673e9985db20c8", + "url": "https://api.github.com/repos/doctrine/sql-formatter/zipball/a8af23a8e9d622505baa2997465782cbe8bb7fc7", + "reference": "a8af23a8e9d622505baa2997465782cbe8bb7fc7", "shasum": "" }, "require": { "php": "^8.1" }, "require-dev": { - "doctrine/coding-standard": "^12", - "ergebnis/phpunit-slow-test-detector": "^2.14", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^10.5" + "doctrine/coding-standard": "^14", + "ergebnis/phpunit-slow-test-detector": "^2.20", + "phpstan/phpstan": "^2.1.31", + "phpunit/phpunit": "^10.5.58" }, "bin": [ "bin/sql-formatter" @@ -1979,9 +1977,9 @@ ], "support": { "issues": "https://github.com/doctrine/sql-formatter/issues", - "source": "https://github.com/doctrine/sql-formatter/tree/1.5.2" + "source": "https://github.com/doctrine/sql-formatter/tree/1.5.3" }, - "time": "2025-01-24T11:45:48+00:00" + "time": "2025-10-26T09:35:14+00:00" }, { "name": "evenement/evenement", @@ -2032,16 +2030,16 @@ }, { "name": "fidry/cpu-core-counter", - "version": "1.2.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/theofidry/cpu-core-counter.git", - "reference": "8520451a140d3f46ac33042715115e290cf5785f" + "reference": "db9508f7b1474469d9d3c53b86f817e344732678" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", - "reference": "8520451a140d3f46ac33042715115e290cf5785f", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678", "shasum": "" }, "require": { @@ -2051,10 +2049,10 @@ "fidry/makefile": "^0.2.0", "fidry/php-cs-fixer-config": "^1.1.2", "phpstan/extension-installer": "^1.2.0", - "phpstan/phpstan": "^1.9.2", - "phpstan/phpstan-deprecation-rules": "^1.0.0", - "phpstan/phpstan-phpunit": "^1.2.2", - "phpstan/phpstan-strict-rules": "^1.4.4", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", "phpunit/phpunit": "^8.5.31 || ^9.5.26", "webmozarts/strict-phpunit": "^7.5" }, @@ -2081,7 +2079,7 @@ ], "support": { "issues": "https://github.com/theofidry/cpu-core-counter/issues", - "source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0" + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0" }, "funding": [ { @@ -2089,7 +2087,7 @@ "type": "github" } ], - "time": "2024-08-06T10:04:20+00:00" + "time": "2025-08-14T07:29:31+00:00" }, { "name": "firebase/php-jwt", @@ -2156,59 +2154,57 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.76.0", + "version": "v3.90.0", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "0e3c484cef0ae9314b0f85986a36296087432c40" + "reference": "ad732c2e9299c9743f9c55ae53cc0e7642ab1155" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/0e3c484cef0ae9314b0f85986a36296087432c40", - "reference": "0e3c484cef0ae9314b0f85986a36296087432c40", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/ad732c2e9299c9743f9c55ae53cc0e7642ab1155", + "reference": "ad732c2e9299c9743f9c55ae53cc0e7642ab1155", "shasum": "" }, "require": { - "clue/ndjson-react": "^1.0", + "clue/ndjson-react": "^1.3", "composer/semver": "^3.4", "composer/xdebug-handler": "^3.0.5", "ext-filter": "*", "ext-hash": "*", "ext-json": "*", "ext-tokenizer": "*", - "fidry/cpu-core-counter": "^1.2", + "fidry/cpu-core-counter": "^1.3", "php": "^7.4 || ^8.0", "react/child-process": "^0.6.6", - "react/event-loop": "^1.0", - "react/promise": "^2.11 || ^3.0", - "react/socket": "^1.0", - "react/stream": "^1.0", + "react/event-loop": "^1.5", + "react/socket": "^1.16", + "react/stream": "^1.4", "sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0", - "symfony/console": "^5.4.45 || ^6.4.13 || ^7.0", - "symfony/event-dispatcher": "^5.4.45 || ^6.4.13 || ^7.0", - "symfony/filesystem": "^5.4.45 || ^6.4.13 || ^7.0", - "symfony/finder": "^5.4.45 || ^6.4.17 || ^7.0", - "symfony/options-resolver": "^5.4.45 || ^6.4.16 || ^7.0", - "symfony/polyfill-mbstring": "^1.32", - "symfony/polyfill-php80": "^1.32", - "symfony/polyfill-php81": "^1.32", - "symfony/process": "^5.4.47 || ^6.4.20 || ^7.2", - "symfony/stopwatch": "^5.4.45 || ^6.4.19 || ^7.0" - }, - "require-dev": { - "facile-it/paraunit": "^1.3.1 || ^2.6", - "infection/infection": "^0.29.14", - "justinrainbow/json-schema": "^5.3 || ^6.4", + "symfony/console": "^5.4.47 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/event-dispatcher": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/finder": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/options-resolver": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/polyfill-mbstring": "^1.33", + "symfony/polyfill-php80": "^1.33", + "symfony/polyfill-php81": "^1.33", + "symfony/polyfill-php84": "^1.33", + "symfony/process": "^5.4.47 || ^6.4.24 || ^7.2 || ^8.0", + "symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0" + }, + "require-dev": { + "facile-it/paraunit": "^1.3.1 || ^2.7", + "infection/infection": "^0.31.0", + "justinrainbow/json-schema": "^6.5", "keradus/cli-executor": "^2.2", "mikey179/vfsstream": "^1.6.12", - "php-coveralls/php-coveralls": "^2.8", - "php-cs-fixer/accessible-object": "^1.1", + "php-coveralls/php-coveralls": "^2.9", "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", - "phpunit/phpunit": "^9.6.23 || ^10.5.47 || ^11.5.25", - "symfony/polyfill-php84": "^1.32", - "symfony/var-dumper": "^5.4.48 || ^6.4.23 || ^7.3.1", - "symfony/yaml": "^5.4.45 || ^6.4.23 || ^7.3.1" + "phpunit/phpunit": "^9.6.25 || ^10.5.53 || ^11.5.34", + "symfony/var-dumper": "^5.4.48 || ^6.4.24 || ^7.3.2 || ^8.0", + "symfony/yaml": "^5.4.45 || ^6.4.24 || ^7.3.2 || ^8.0" }, "suggest": { "ext-dom": "For handling output formats in XML", @@ -2249,7 +2245,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.76.0" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.90.0" }, "funding": [ { @@ -2257,26 +2253,26 @@ "type": "github" } ], - "time": "2025-06-30T14:15:06+00:00" + "time": "2025-11-20T15:15:16+00:00" }, { "name": "friendsofphp/proxy-manager-lts", - "version": "v1.0.18", + "version": "v1.0.19", "source": { "type": "git", "url": "https://github.com/FriendsOfPHP/proxy-manager-lts.git", - "reference": "2c8a6cffc3220e99352ad958fe7cf06bf6f7690f" + "reference": "c20299aa9f48a622052964a75c5a4cef017398b2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/proxy-manager-lts/zipball/2c8a6cffc3220e99352ad958fe7cf06bf6f7690f", - "reference": "2c8a6cffc3220e99352ad958fe7cf06bf6f7690f", + "url": "https://api.github.com/repos/FriendsOfPHP/proxy-manager-lts/zipball/c20299aa9f48a622052964a75c5a4cef017398b2", + "reference": "c20299aa9f48a622052964a75c5a4cef017398b2", "shasum": "" }, "require": { "laminas/laminas-code": "~3.4.1|^4.0", "php": ">=7.1", - "symfony/filesystem": "^4.4.17|^5.0|^6.0|^7.0" + "symfony/filesystem": "^4.4.17|^5.0|^6.0|^7.0|^8.0" }, "conflict": { "laminas/laminas-stdlib": "<3.2.1", @@ -2327,7 +2323,7 @@ ], "support": { "issues": "https://github.com/FriendsOfPHP/proxy-manager-lts/issues", - "source": "https://github.com/FriendsOfPHP/proxy-manager-lts/tree/v1.0.18" + "source": "https://github.com/FriendsOfPHP/proxy-manager-lts/tree/v1.0.19" }, "funding": [ { @@ -2339,7 +2335,7 @@ "type": "tidelift" } ], - "time": "2024-03-20T12:50:41+00:00" + "time": "2025-10-28T10:28:17+00:00" }, { "name": "gesdinet/jwt-refresh-token-bundle", @@ -2423,22 +2419,22 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.9.3", + "version": "7.10.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77" + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", - "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0.3", - "guzzlehttp/psr7": "^2.7.0", + "guzzlehttp/promises": "^2.3", + "guzzlehttp/psr7": "^2.8", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" @@ -2529,7 +2525,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.9.3" + "source": "https://github.com/guzzle/guzzle/tree/7.10.0" }, "funding": [ { @@ -2545,20 +2541,20 @@ "type": "tidelift" } ], - "time": "2025-03-27T13:37:11+00:00" + "time": "2025-08-23T22:36:01+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.2.0", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c" + "reference": "481557b130ef3790cf82b713667b43030dc9c957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/7c69f28996b0a6920945dd20b3857e499d9ca96c", - "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c", + "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", + "reference": "481557b130ef3790cf82b713667b43030dc9c957", "shasum": "" }, "require": { @@ -2566,7 +2562,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.39 || ^9.6.20" + "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "type": "library", "extra": { @@ -2612,7 +2608,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.2.0" + "source": "https://github.com/guzzle/promises/tree/2.3.0" }, "funding": [ { @@ -2628,20 +2624,20 @@ "type": "tidelift" } ], - "time": "2025-03-27T13:27:01+00:00" + "time": "2025-08-22T14:34:08+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.7.1", + "version": "2.8.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16" + "reference": "21dc724a0583619cd1652f673303492272778051" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/c2270caaabe631b3b44c85f99e5a04bbb8060d16", - "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051", + "reference": "21dc724a0583619cd1652f673303492272778051", "shasum": "" }, "require": { @@ -2657,7 +2653,7 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "http-interop/http-factory-tests": "0.9.0", - "phpunit/phpunit": "^8.5.39 || ^9.6.20" + "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -2728,7 +2724,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.7.1" + "source": "https://github.com/guzzle/psr7/tree/2.8.0" }, "funding": [ { @@ -2744,7 +2740,7 @@ "type": "tidelift" } ], - "time": "2025-03-27T12:30:47+00:00" + "time": "2025-08-23T21:21:41+00:00" }, { "name": "imagine/imagine", @@ -3065,26 +3061,26 @@ }, { "name": "kubawerlos/php-cs-fixer-custom-fixers", - "version": "v3.27.2", + "version": "v3.35.1", "source": { "type": "git", "url": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers.git", - "reference": "09a29566891d4214da2c1c429e7857f40397ef50" + "reference": "2a35f80ae24ca77443a7af1599c3a3db1b6bd395" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/kubawerlos/php-cs-fixer-custom-fixers/zipball/09a29566891d4214da2c1c429e7857f40397ef50", - "reference": "09a29566891d4214da2c1c429e7857f40397ef50", + "url": "https://api.github.com/repos/kubawerlos/php-cs-fixer-custom-fixers/zipball/2a35f80ae24ca77443a7af1599c3a3db1b6bd395", + "reference": "2a35f80ae24ca77443a7af1599c3a3db1b6bd395", "shasum": "" }, "require": { "ext-filter": "*", "ext-tokenizer": "*", - "friendsofphp/php-cs-fixer": "^3.61.1", + "friendsofphp/php-cs-fixer": "^3.87", "php": "^7.4 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^9.6.22 || 10.5.45 || ^11.5.7" + "phpunit/phpunit": "^9.6.24 || ^10.5.51 || ^11.5.32" }, "type": "library", "autoload": { @@ -3105,7 +3101,7 @@ "description": "A set of custom fixers for PHP CS Fixer", "support": { "issues": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/issues", - "source": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/tree/v3.27.2" + "source": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/tree/v3.35.1" }, "funding": [ { @@ -3113,31 +3109,31 @@ "type": "github" } ], - "time": "2025-06-28T11:13:24+00:00" + "time": "2025-09-28T18:43:35+00:00" }, { "name": "laminas/laminas-code", - "version": "4.16.0", + "version": "4.17.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-code.git", - "reference": "1793e78dad4108b594084d05d1fb818b85b110af" + "reference": "40d61e2899ec17c5d08bbc0a2d586b3ca17ab9bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-code/zipball/1793e78dad4108b594084d05d1fb818b85b110af", - "reference": "1793e78dad4108b594084d05d1fb818b85b110af", + "url": "https://api.github.com/repos/laminas/laminas-code/zipball/40d61e2899ec17c5d08bbc0a2d586b3ca17ab9bd", + "reference": "40d61e2899ec17c5d08bbc0a2d586b3ca17ab9bd", "shasum": "" }, "require": { - "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" }, "require-dev": { "doctrine/annotations": "^2.0.1", "ext-phar": "*", "laminas/laminas-coding-standard": "^3.0.0", "laminas/laminas-stdlib": "^3.18.0", - "phpunit/phpunit": "^10.5.37", + "phpunit/phpunit": "^10.5.58", "psalm/plugin-phpunit": "^0.19.0", "vimeo/psalm": "^5.15.0" }, @@ -3176,38 +3172,38 @@ "type": "community_bridge" } ], - "time": "2024-11-20T13:15:13+00:00" + "time": "2025-11-01T09:38:14+00:00" }, { "name": "lcobucci/clock", - "version": "3.3.1", + "version": "3.5.0", "source": { "type": "git", "url": "https://github.com/lcobucci/clock.git", - "reference": "db3713a61addfffd615b79bf0bc22f0ccc61b86b" + "reference": "a3139d9e97d47826f27e6a17bb63f13621f86058" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lcobucci/clock/zipball/db3713a61addfffd615b79bf0bc22f0ccc61b86b", - "reference": "db3713a61addfffd615b79bf0bc22f0ccc61b86b", + "url": "https://api.github.com/repos/lcobucci/clock/zipball/a3139d9e97d47826f27e6a17bb63f13621f86058", + "reference": "a3139d9e97d47826f27e6a17bb63f13621f86058", "shasum": "" }, "require": { - "php": "~8.2.0 || ~8.3.0 || ~8.4.0", + "php": "~8.3.0 || ~8.4.0 || ~8.5.0", "psr/clock": "^1.0" }, "provide": { "psr/clock-implementation": "1.0" }, "require-dev": { - "infection/infection": "^0.29", - "lcobucci/coding-standard": "^11.1.0", + "infection/infection": "^0.31", + "lcobucci/coding-standard": "^11.2.0", "phpstan/extension-installer": "^1.3.1", - "phpstan/phpstan": "^1.10.25", - "phpstan/phpstan-deprecation-rules": "^1.1.3", - "phpstan/phpstan-phpunit": "^1.3.13", - "phpstan/phpstan-strict-rules": "^1.5.1", - "phpunit/phpunit": "^11.3.6" + "phpstan/phpstan": "^2.0.0", + "phpstan/phpstan-deprecation-rules": "^2.0.0", + "phpstan/phpstan-phpunit": "^2.0.0", + "phpstan/phpstan-strict-rules": "^2.0.0", + "phpunit/phpunit": "^12.0.0" }, "type": "library", "autoload": { @@ -3228,7 +3224,7 @@ "description": "Yet another clock abstraction", "support": { "issues": "https://github.com/lcobucci/clock/issues", - "source": "https://github.com/lcobucci/clock/tree/3.3.1" + "source": "https://github.com/lcobucci/clock/tree/3.5.0" }, "funding": [ { @@ -3240,26 +3236,26 @@ "type": "patreon" } ], - "time": "2024-09-24T20:45:14+00:00" + "time": "2025-10-27T09:03:17+00:00" }, { "name": "lcobucci/jwt", - "version": "5.5.0", + "version": "5.6.0", "source": { "type": "git", "url": "https://github.com/lcobucci/jwt.git", - "reference": "a835af59b030d3f2967725697cf88300f579088e" + "reference": "bb3e9f21e4196e8afc41def81ef649c164bca25e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lcobucci/jwt/zipball/a835af59b030d3f2967725697cf88300f579088e", - "reference": "a835af59b030d3f2967725697cf88300f579088e", + "url": "https://api.github.com/repos/lcobucci/jwt/zipball/bb3e9f21e4196e8afc41def81ef649c164bca25e", + "reference": "bb3e9f21e4196e8afc41def81ef649c164bca25e", "shasum": "" }, "require": { "ext-openssl": "*", "ext-sodium": "*", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", "psr/clock": "^1.0" }, "require-dev": { @@ -3301,7 +3297,7 @@ ], "support": { "issues": "https://github.com/lcobucci/jwt/issues", - "source": "https://github.com/lcobucci/jwt/tree/5.5.0" + "source": "https://github.com/lcobucci/jwt/tree/5.6.0" }, "funding": [ { @@ -3313,26 +3309,26 @@ "type": "patreon" } ], - "time": "2025-01-26T21:29:45+00:00" + "time": "2025-10-17T11:30:53+00:00" }, { "name": "league/oauth2-client", - "version": "2.8.1", + "version": "2.9.0", "source": { "type": "git", "url": "https://github.com/thephpleague/oauth2-client.git", - "reference": "9df2924ca644736c835fc60466a3a60390d334f9" + "reference": "26e8c5da4f3d78cede7021e09b1330a0fc093d5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/9df2924ca644736c835fc60466a3a60390d334f9", - "reference": "9df2924ca644736c835fc60466a3a60390d334f9", + "url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/26e8c5da4f3d78cede7021e09b1330a0fc093d5e", + "reference": "26e8c5da4f3d78cede7021e09b1330a0fc093d5e", "shasum": "" }, "require": { "ext-json": "*", "guzzlehttp/guzzle": "^6.5.8 || ^7.4.5", - "php": "^7.1 || >=8.0.0 <8.5.0" + "php": "^7.1 || >=8.0.0 <8.6.0" }, "require-dev": { "mockery/mockery": "^1.3.5", @@ -3376,9 +3372,9 @@ ], "support": { "issues": "https://github.com/thephpleague/oauth2-client/issues", - "source": "https://github.com/thephpleague/oauth2-client/tree/2.8.1" + "source": "https://github.com/thephpleague/oauth2-client/tree/2.9.0" }, - "time": "2025-02-26T04:37:30+00:00" + "time": "2025-11-25T22:17:17+00:00" }, { "name": "lexik/jwt-authentication-bundle", @@ -3501,22 +3497,23 @@ }, { "name": "liip/imagine-bundle", - "version": "2.13.3", + "version": "2.15.0", "source": { "type": "git", "url": "https://github.com/liip/LiipImagineBundle.git", - "reference": "3faccde327f91368e51d05ecad49a9cd915abd81" + "reference": "f8c98a5a962806f26571db219412b64266c763d8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/liip/LiipImagineBundle/zipball/3faccde327f91368e51d05ecad49a9cd915abd81", - "reference": "3faccde327f91368e51d05ecad49a9cd915abd81", + "url": "https://api.github.com/repos/liip/LiipImagineBundle/zipball/f8c98a5a962806f26571db219412b64266c763d8", + "reference": "f8c98a5a962806f26571db219412b64266c763d8", "shasum": "" }, "require": { "ext-mbstring": "*", "imagine/imagine": "^1.3.2", "php": "^7.2|^8.0", + "symfony/deprecation-contracts": "^2.5 || ^3", "symfony/filesystem": "^3.4|^4.4|^5.3|^6.0|^7.0", "symfony/finder": "^3.4|^4.4|^5.3|^6.0|^7.0", "symfony/framework-bundle": "^3.4.23|^4.4|^5.3|^6.0|^7.0", @@ -3601,22 +3598,22 @@ ], "support": { "issues": "https://github.com/liip/LiipImagineBundle/issues", - "source": "https://github.com/liip/LiipImagineBundle/tree/2.13.3" + "source": "https://github.com/liip/LiipImagineBundle/tree/2.15.0" }, - "time": "2024-12-12T09:38:23+00:00" + "time": "2025-10-09T06:49:28+00:00" }, { "name": "masterminds/html5", - "version": "2.9.0", + "version": "2.10.0", "source": { "type": "git", "url": "https://github.com/Masterminds/html5-php.git", - "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6" + "reference": "fcf91eb64359852f00d921887b219479b4f21251" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f5ac2c0b0a2eefca70b2ce32a5809992227e75a6", - "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/fcf91eb64359852f00d921887b219479b4f21251", + "reference": "fcf91eb64359852f00d921887b219479b4f21251", "shasum": "" }, "require": { @@ -3668,9 +3665,9 @@ ], "support": { "issues": "https://github.com/Masterminds/html5-php/issues", - "source": "https://github.com/Masterminds/html5-php/tree/2.9.0" + "source": "https://github.com/Masterminds/html5-php/tree/2.10.0" }, - "time": "2024-03-31T07:05:07+00:00" + "time": "2025-07-25T09:04:22+00:00" }, { "name": "monolog/monolog", @@ -3844,25 +3841,28 @@ }, { "name": "nelmio/cors-bundle", - "version": "2.5.0", + "version": "2.6.0", "source": { "type": "git", "url": "https://github.com/nelmio/NelmioCorsBundle.git", - "reference": "3a526fe025cd20e04a6a11370cf5ab28dbb5a544" + "reference": "530217472204881cacd3671909f634b960c7b948" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nelmio/NelmioCorsBundle/zipball/3a526fe025cd20e04a6a11370cf5ab28dbb5a544", - "reference": "3a526fe025cd20e04a6a11370cf5ab28dbb5a544", + "url": "https://api.github.com/repos/nelmio/NelmioCorsBundle/zipball/530217472204881cacd3671909f634b960c7b948", + "reference": "530217472204881cacd3671909f634b960c7b948", "shasum": "" }, "require": { "psr/log": "^1.0 || ^2.0 || ^3.0", - "symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0" + "symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0 || ^8.0" }, "require-dev": { - "mockery/mockery": "^1.3.6", - "symfony/phpunit-bridge": "^5.4 || ^6.0 || ^7.0" + "phpstan/phpstan": "^1.11.5", + "phpstan/phpstan-deprecation-rules": "^1.2.0", + "phpstan/phpstan-phpunit": "^1.4", + "phpstan/phpstan-symfony": "^1.4.4", + "phpunit/phpunit": "^8" }, "type": "symfony-bundle", "extra": { @@ -3900,9 +3900,9 @@ ], "support": { "issues": "https://github.com/nelmio/NelmioCorsBundle/issues", - "source": "https://github.com/nelmio/NelmioCorsBundle/tree/2.5.0" + "source": "https://github.com/nelmio/NelmioCorsBundle/tree/2.6.0" }, - "time": "2024-06-24T21:25:28+00:00" + "time": "2025-10-23T06:57:22+00:00" }, { "name": "nyholm/psr7", @@ -3984,35 +3984,35 @@ }, { "name": "pentatrion/vite-bundle", - "version": "v8.2.0", + "version": "v8.2.3", "source": { "type": "git", "url": "https://github.com/lhapaipai/vite-bundle.git", - "reference": "e4cc14b4586cff1d55f51f6eebe6f62a0d9d7b17" + "reference": "354d90eebc702950daba6adcd2b93930de61812c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lhapaipai/vite-bundle/zipball/e4cc14b4586cff1d55f51f6eebe6f62a0d9d7b17", - "reference": "e4cc14b4586cff1d55f51f6eebe6f62a0d9d7b17", + "url": "https://api.github.com/repos/lhapaipai/vite-bundle/zipball/354d90eebc702950daba6adcd2b93930de61812c", + "reference": "354d90eebc702950daba6adcd2b93930de61812c", "shasum": "" }, "require": { "php": "^8.0", - "symfony/asset": "^4.4 || ^5.0 || ^6.0 || ^7.0", - "symfony/config": "^4.4 || ^5.0 || ^6.0 || ^7.0", - "symfony/dependency-injection": "^4.4 || ^5.0 || ^6.0 || ^7.0", - "symfony/framework-bundle": "^4.4 || ^5.0 || ^6.0 || ^7.0", - "symfony/http-client": "^4.4 || ^5.0 || ^6.0 || ^7.0", - "symfony/http-kernel": "^4.4 || ^5.0 || ^6.0 || ^7.0", - "symfony/twig-bundle": "^4.4 || ^5.0 || ^6.0 || ^7.0" + "symfony/asset": "^4.4 || ^5.0 || ^6.0 || ^7.0 || ^8.0", + "symfony/config": "^4.4 || ^5.0 || ^6.0 || ^7.0 || ^8.0", + "symfony/dependency-injection": "^4.4 || ^5.0 || ^6.0 || ^7.0 || ^8.0", + "symfony/framework-bundle": "^4.4 || ^5.0 || ^6.0 || ^7.0 || ^8.0", + "symfony/http-client": "^4.4 || ^5.0 || ^6.0 || ^7.0 || ^8.0", + "symfony/http-kernel": "^4.4 || ^5.0 || ^6.0 || ^7.0 || ^8.0", + "symfony/twig-bundle": "^4.4 || ^5.0 || ^6.0 || ^7.0 || ^8.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^3.9", "phpstan/phpstan": "^1.8", "phpstan/phpstan-symfony": "^1.3", "phpunit/phpunit": "^9.5", - "symfony/phpunit-bridge": "^4.4 || ^5.0 || ^6.0 || ^7.0", - "symfony/web-link": "^4.4 || ^5.0 || ^6.0 || ^7.0" + "symfony/phpunit-bridge": "^4.4 || ^5.0 || ^6.0 || ^7.0 || ^8.0", + "symfony/web-link": "^4.4 || ^5.0 || ^6.0 || ^7.0 || ^8.0" }, "type": "symfony-bundle", "autoload": { @@ -4039,9 +4039,9 @@ ], "support": { "issues": "https://github.com/lhapaipai/vite-bundle/issues", - "source": "https://github.com/lhapaipai/vite-bundle/tree/v8.2.0" + "source": "https://github.com/lhapaipai/vite-bundle/tree/v8.2.3" }, - "time": "2025-06-24T10:50:50+00:00" + "time": "2025-11-16T22:02:23+00:00" }, { "name": "php-http/discovery", @@ -4286,16 +4286,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.6.2", + "version": "5.6.5", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "92dde6a5919e34835c506ac8c523ef095a95ed62" + "reference": "90614c73d3800e187615e2dd236ad0e2a01bf761" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/92dde6a5919e34835c506ac8c523ef095a95ed62", - "reference": "92dde6a5919e34835c506ac8c523ef095a95ed62", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/90614c73d3800e187615e2dd236ad0e2a01bf761", + "reference": "90614c73d3800e187615e2dd236ad0e2a01bf761", "shasum": "" }, "require": { @@ -4344,22 +4344,22 @@ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.2" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.5" }, - "time": "2025-04-13T19:20:35+00:00" + "time": "2025-11-27T19:50:05+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.10.0", + "version": "1.12.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a" + "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/679e3ce485b99e84c775d28e2e96fade9a7fb50a", - "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/92a98ada2b93d9b201a613cb5a33584dde25f195", + "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195", "shasum": "" }, "require": { @@ -4402,9 +4402,9 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.10.0" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.12.0" }, - "time": "2024-11-09T15:12:26+00:00" + "time": "2025-11-21T15:09:14+00:00" }, { "name": "phpstan/phpdoc-parser", @@ -5112,16 +5112,16 @@ }, { "name": "react/dns", - "version": "v1.13.0", + "version": "v1.14.0", "source": { "type": "git", "url": "https://github.com/reactphp/dns.git", - "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5" + "reference": "7562c05391f42701c1fccf189c8225fece1cd7c3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/dns/zipball/eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", - "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", + "url": "https://api.github.com/repos/reactphp/dns/zipball/7562c05391f42701c1fccf189c8225fece1cd7c3", + "reference": "7562c05391f42701c1fccf189c8225fece1cd7c3", "shasum": "" }, "require": { @@ -5176,7 +5176,7 @@ ], "support": { "issues": "https://github.com/reactphp/dns/issues", - "source": "https://github.com/reactphp/dns/tree/v1.13.0" + "source": "https://github.com/reactphp/dns/tree/v1.14.0" }, "funding": [ { @@ -5184,20 +5184,20 @@ "type": "open_collective" } ], - "time": "2024-06-13T14:18:03+00:00" + "time": "2025-11-18T19:34:28+00:00" }, { "name": "react/event-loop", - "version": "v1.5.0", + "version": "v1.6.0", "source": { "type": "git", "url": "https://github.com/reactphp/event-loop.git", - "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354" + "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/event-loop/zipball/bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", - "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/ba276bda6083df7e0050fd9b33f66ad7a4ac747a", + "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a", "shasum": "" }, "require": { @@ -5248,7 +5248,7 @@ ], "support": { "issues": "https://github.com/reactphp/event-loop/issues", - "source": "https://github.com/reactphp/event-loop/tree/v1.5.0" + "source": "https://github.com/reactphp/event-loop/tree/v1.6.0" }, "funding": [ { @@ -5256,27 +5256,27 @@ "type": "open_collective" } ], - "time": "2023-11-13T13:48:05+00:00" + "time": "2025-11-17T20:46:25+00:00" }, { "name": "react/promise", - "version": "v3.2.0", + "version": "v3.3.0", "source": { "type": "git", "url": "https://github.com/reactphp/promise.git", - "reference": "8a164643313c71354582dc850b42b33fa12a4b63" + "reference": "23444f53a813a3296c1368bb104793ce8d88f04a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/8a164643313c71354582dc850b42b33fa12a4b63", - "reference": "8a164643313c71354582dc850b42b33fa12a4b63", + "url": "https://api.github.com/repos/reactphp/promise/zipball/23444f53a813a3296c1368bb104793ce8d88f04a", + "reference": "23444f53a813a3296c1368bb104793ce8d88f04a", "shasum": "" }, "require": { "php": ">=7.1.0" }, "require-dev": { - "phpstan/phpstan": "1.10.39 || 1.4.10", + "phpstan/phpstan": "1.12.28 || 1.4.10", "phpunit/phpunit": "^9.6 || ^7.5" }, "type": "library", @@ -5321,7 +5321,7 @@ ], "support": { "issues": "https://github.com/reactphp/promise/issues", - "source": "https://github.com/reactphp/promise/tree/v3.2.0" + "source": "https://github.com/reactphp/promise/tree/v3.3.0" }, "funding": [ { @@ -5329,20 +5329,20 @@ "type": "open_collective" } ], - "time": "2024-05-24T10:39:05+00:00" + "time": "2025-08-19T18:57:03+00:00" }, { "name": "react/socket", - "version": "v1.16.0", + "version": "v1.17.0", "source": { "type": "git", "url": "https://github.com/reactphp/socket.git", - "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1" + "reference": "ef5b17b81f6f60504c539313f94f2d826c5faa08" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/socket/zipball/23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", - "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", + "url": "https://api.github.com/repos/reactphp/socket/zipball/ef5b17b81f6f60504c539313f94f2d826c5faa08", + "reference": "ef5b17b81f6f60504c539313f94f2d826c5faa08", "shasum": "" }, "require": { @@ -5401,7 +5401,7 @@ ], "support": { "issues": "https://github.com/reactphp/socket/issues", - "source": "https://github.com/reactphp/socket/tree/v1.16.0" + "source": "https://github.com/reactphp/socket/tree/v1.17.0" }, "funding": [ { @@ -5409,7 +5409,7 @@ "type": "open_collective" } ], - "time": "2024-07-26T10:38:09+00:00" + "time": "2025-11-19T20:47:34+00:00" }, { "name": "react/stream", @@ -5648,16 +5648,16 @@ }, { "name": "symfony/asset", - "version": "v6.4.13", + "version": "v6.4.24", "source": { "type": "git", "url": "https://github.com/symfony/asset.git", - "reference": "2466c17d61d14539cddf77e57ebb9cc971185302" + "reference": "cfee7c0d64be113383db74a2fdd65d426b7f3aab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/asset/zipball/2466c17d61d14539cddf77e57ebb9cc971185302", - "reference": "2466c17d61d14539cddf77e57ebb9cc971185302", + "url": "https://api.github.com/repos/symfony/asset/zipball/cfee7c0d64be113383db74a2fdd65d426b7f3aab", + "reference": "cfee7c0d64be113383db74a2fdd65d426b7f3aab", "shasum": "" }, "require": { @@ -5697,7 +5697,7 @@ "description": "Manages URL generation and versioning of web assets such as CSS stylesheets, JavaScript files and image files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/asset/tree/v6.4.13" + "source": "https://github.com/symfony/asset/tree/v6.4.24" }, "funding": [ { @@ -5708,25 +5708,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-10-25T15:07:50+00:00" + "time": "2025-07-10T08:14:14+00:00" }, { "name": "symfony/cache", - "version": "v6.4.23", + "version": "v6.4.28", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "c88690befb8d4a85dc321fb78d677507f5eb141b" + "reference": "31628f36fc97c5714d181b3a8d29efb85c6a7677" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/c88690befb8d4a85dc321fb78d677507f5eb141b", - "reference": "c88690befb8d4a85dc321fb78d677507f5eb141b", + "url": "https://api.github.com/repos/symfony/cache/zipball/31628f36fc97c5714d181b3a8d29efb85c6a7677", + "reference": "31628f36fc97c5714d181b3a8d29efb85c6a7677", "shasum": "" }, "require": { @@ -5793,7 +5797,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v6.4.23" + "source": "https://github.com/symfony/cache/tree/v6.4.28" }, "funding": [ { @@ -5804,12 +5808,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-27T18:31:36+00:00" + "time": "2025-10-30T08:37:02+00:00" }, { "name": "symfony/cache-contracts", @@ -5889,16 +5897,16 @@ }, { "name": "symfony/clock", - "version": "v6.4.13", + "version": "v6.4.24", "source": { "type": "git", "url": "https://github.com/symfony/clock.git", - "reference": "b2bf55c4dd115003309eafa87ee7df9ed3dde81b" + "reference": "5e15a9c9aeeb44a99f7cf24aa75aa9607795f6f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/clock/zipball/b2bf55c4dd115003309eafa87ee7df9ed3dde81b", - "reference": "b2bf55c4dd115003309eafa87ee7df9ed3dde81b", + "url": "https://api.github.com/repos/symfony/clock/zipball/5e15a9c9aeeb44a99f7cf24aa75aa9607795f6f8", + "reference": "5e15a9c9aeeb44a99f7cf24aa75aa9607795f6f8", "shasum": "" }, "require": { @@ -5943,7 +5951,7 @@ "time" ], "support": { - "source": "https://github.com/symfony/clock/tree/v6.4.13" + "source": "https://github.com/symfony/clock/tree/v6.4.24" }, "funding": [ { @@ -5954,25 +5962,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:18:03+00:00" + "time": "2025-07-10T08:14:14+00:00" }, { "name": "symfony/config", - "version": "v6.4.22", + "version": "v6.4.28", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "af5917a3b1571f54689e56677a3f06440d2fe4c7" + "reference": "15947c18ef3ddb0b2f4ec936b9e90e2520979f62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/af5917a3b1571f54689e56677a3f06440d2fe4c7", - "reference": "af5917a3b1571f54689e56677a3f06440d2fe4c7", + "url": "https://api.github.com/repos/symfony/config/zipball/15947c18ef3ddb0b2f4ec936b9e90e2520979f62", + "reference": "15947c18ef3ddb0b2f4ec936b9e90e2520979f62", "shasum": "" }, "require": { @@ -6018,7 +6030,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v6.4.22" + "source": "https://github.com/symfony/config/tree/v6.4.28" }, "funding": [ { @@ -6029,25 +6041,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-05-14T06:00:01+00:00" + "time": "2025-11-01T19:52:02+00:00" }, { "name": "symfony/console", - "version": "v6.4.23", + "version": "v6.4.27", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "9056771b8eca08d026cd3280deeec3cfd99c4d93" + "reference": "13d3176cf8ad8ced24202844e9f95af11e2959fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/9056771b8eca08d026cd3280deeec3cfd99c4d93", - "reference": "9056771b8eca08d026cd3280deeec3cfd99c4d93", + "url": "https://api.github.com/repos/symfony/console/zipball/13d3176cf8ad8ced24202844e9f95af11e2959fc", + "reference": "13d3176cf8ad8ced24202844e9f95af11e2959fc", "shasum": "" }, "require": { @@ -6112,7 +6128,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.4.23" + "source": "https://github.com/symfony/console/tree/v6.4.27" }, "funding": [ { @@ -6123,25 +6139,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-27T19:37:22+00:00" + "time": "2025-10-06T10:25:16+00:00" }, { "name": "symfony/dependency-injection", - "version": "v6.4.23", + "version": "v6.4.26", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "0d9f24f3de0a83573fce5c9ed025d6306c6e166b" + "reference": "5f311eaf0b321f8ec640f6bae12da43a14026898" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/0d9f24f3de0a83573fce5c9ed025d6306c6e166b", - "reference": "0d9f24f3de0a83573fce5c9ed025d6306c6e166b", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/5f311eaf0b321f8ec640f6bae12da43a14026898", + "reference": "5f311eaf0b321f8ec640f6bae12da43a14026898", "shasum": "" }, "require": { @@ -6193,7 +6213,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v6.4.23" + "source": "https://github.com/symfony/dependency-injection/tree/v6.4.26" }, "funding": [ { @@ -6204,12 +6224,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-23T06:49:06+00:00" + "time": "2025-09-11T09:57:09+00:00" }, { "name": "symfony/deprecation-contracts", @@ -6280,16 +6304,16 @@ }, { "name": "symfony/doctrine-bridge", - "version": "v6.4.23", + "version": "v6.4.26", "source": { "type": "git", "url": "https://github.com/symfony/doctrine-bridge.git", - "reference": "ef360932b8b342c0360b768b97776a12d5242db6" + "reference": "c14bb5a9125c411e73354954940e06b6e7fcc344" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/ef360932b8b342c0360b768b97776a12d5242db6", - "reference": "ef360932b8b342c0360b768b97776a12d5242db6", + "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/c14bb5a9125c411e73354954940e06b6e7fcc344", + "reference": "c14bb5a9125c411e73354954940e06b6e7fcc344", "shasum": "" }, "require": { @@ -6368,7 +6392,7 @@ "description": "Provides integration for Doctrine with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/doctrine-bridge/tree/v6.4.23" + "source": "https://github.com/symfony/doctrine-bridge/tree/v6.4.26" }, "funding": [ { @@ -6379,25 +6403,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-26T11:46:10+00:00" + "time": "2025-09-26T15:07:38+00:00" }, { "name": "symfony/dom-crawler", - "version": "v6.4.23", + "version": "v6.4.25", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "22210aacb35dbadd772325d759d17bce2374a84d" + "reference": "976302990f9f2a6d4c07206836dd4ca77cae9524" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/22210aacb35dbadd772325d759d17bce2374a84d", - "reference": "22210aacb35dbadd772325d759d17bce2374a84d", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/976302990f9f2a6d4c07206836dd4ca77cae9524", + "reference": "976302990f9f2a6d4c07206836dd4ca77cae9524", "shasum": "" }, "require": { @@ -6435,7 +6463,7 @@ "description": "Eases DOM navigation for HTML and XML documents", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v6.4.23" + "source": "https://github.com/symfony/dom-crawler/tree/v6.4.25" }, "funding": [ { @@ -6446,25 +6474,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-13T12:10:00+00:00" + "time": "2025-08-05T18:56:08+00:00" }, { "name": "symfony/dotenv", - "version": "v6.4.16", + "version": "v6.4.24", "source": { "type": "git", "url": "https://github.com/symfony/dotenv.git", - "reference": "1ac5e7e7e862d4d574258daf08bd569ba926e4a5" + "reference": "234b6c602f12b00693f4b0d1054386fb30dfc8ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dotenv/zipball/1ac5e7e7e862d4d574258daf08bd569ba926e4a5", - "reference": "1ac5e7e7e862d4d574258daf08bd569ba926e4a5", + "url": "https://api.github.com/repos/symfony/dotenv/zipball/234b6c602f12b00693f4b0d1054386fb30dfc8ff", + "reference": "234b6c602f12b00693f4b0d1054386fb30dfc8ff", "shasum": "" }, "require": { @@ -6509,7 +6541,7 @@ "environment" ], "support": { - "source": "https://github.com/symfony/dotenv/tree/v6.4.16" + "source": "https://github.com/symfony/dotenv/tree/v6.4.24" }, "funding": [ { @@ -6520,25 +6552,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-11-27T11:08:19+00:00" + "time": "2025-07-10T08:14:14+00:00" }, { "name": "symfony/error-handler", - "version": "v6.4.23", + "version": "v6.4.26", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "b088e0b175c30b4e06d8085200fa465b586f44fa" + "reference": "41bedcaec5b72640b0ec2096547b75fda72ead6c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/b088e0b175c30b4e06d8085200fa465b586f44fa", - "reference": "b088e0b175c30b4e06d8085200fa465b586f44fa", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/41bedcaec5b72640b0ec2096547b75fda72ead6c", + "reference": "41bedcaec5b72640b0ec2096547b75fda72ead6c", "shasum": "" }, "require": { @@ -6584,7 +6620,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v6.4.23" + "source": "https://github.com/symfony/error-handler/tree/v6.4.26" }, "funding": [ { @@ -6595,25 +6631,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-13T07:39:48+00:00" + "time": "2025-09-11T09:57:09+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v6.4.13", + "version": "v6.4.25", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e" + "reference": "b0cf3162020603587363f0551cd3be43958611ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e", - "reference": "0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b0cf3162020603587363f0551cd3be43958611ff", + "reference": "b0cf3162020603587363f0551cd3be43958611ff", "shasum": "" }, "require": { @@ -6664,7 +6704,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.13" + "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.25" }, "funding": [ { @@ -6675,12 +6715,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:18:03+00:00" + "time": "2025-08-13T09:41:44+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -6760,16 +6804,16 @@ }, { "name": "symfony/expression-language", - "version": "v6.4.13", + "version": "v6.4.24", "source": { "type": "git", "url": "https://github.com/symfony/expression-language.git", - "reference": "3524904fb026356a5230cd197f9a4e6a61e0e7df" + "reference": "1ea0adaa53539ea7e70821ae9de49ebe03ae7091" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/expression-language/zipball/3524904fb026356a5230cd197f9a4e6a61e0e7df", - "reference": "3524904fb026356a5230cd197f9a4e6a61e0e7df", + "url": "https://api.github.com/repos/symfony/expression-language/zipball/1ea0adaa53539ea7e70821ae9de49ebe03ae7091", + "reference": "1ea0adaa53539ea7e70821ae9de49ebe03ae7091", "shasum": "" }, "require": { @@ -6804,7 +6848,7 @@ "description": "Provides an engine that can compile and evaluate expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/expression-language/tree/v6.4.13" + "source": "https://github.com/symfony/expression-language/tree/v6.4.24" }, "funding": [ { @@ -6815,25 +6859,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-10-09T08:40:40+00:00" + "time": "2025-07-10T08:14:14+00:00" }, { "name": "symfony/filesystem", - "version": "v6.4.13", + "version": "v6.4.24", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3" + "reference": "75ae2edb7cdcc0c53766c30b0a2512b8df574bd8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/4856c9cf585d5a0313d8d35afd681a526f038dd3", - "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/75ae2edb7cdcc0c53766c30b0a2512b8df574bd8", + "reference": "75ae2edb7cdcc0c53766c30b0a2512b8df574bd8", "shasum": "" }, "require": { @@ -6870,7 +6918,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.4.13" + "source": "https://github.com/symfony/filesystem/tree/v6.4.24" }, "funding": [ { @@ -6881,25 +6929,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-10-25T15:07:50+00:00" + "time": "2025-07-10T08:14:14+00:00" }, { "name": "symfony/finder", - "version": "v6.4.24", + "version": "v6.4.27", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "73089124388c8510efb8d2d1689285d285937b08" + "reference": "a1b6aa435d2fba50793b994a839c32b6064f063b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/73089124388c8510efb8d2d1689285d285937b08", - "reference": "73089124388c8510efb8d2d1689285d285937b08", + "url": "https://api.github.com/repos/symfony/finder/zipball/a1b6aa435d2fba50793b994a839c32b6064f063b", + "reference": "a1b6aa435d2fba50793b994a839c32b6064f063b", "shasum": "" }, "require": { @@ -6934,7 +6986,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.4.24" + "source": "https://github.com/symfony/finder/tree/v6.4.27" }, "funding": [ { @@ -6954,35 +7006,36 @@ "type": "tidelift" } ], - "time": "2025-07-15T12:02:45+00:00" + "time": "2025-10-15T18:32:00+00:00" }, { "name": "symfony/flex", - "version": "v2.7.1", + "version": "v2.10.0", "source": { "type": "git", "url": "https://github.com/symfony/flex.git", - "reference": "4ae50d368415a06820739e54d38a4a29d6df9155" + "reference": "9cd384775973eabbf6e8b05784dda279fc67c28d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/flex/zipball/4ae50d368415a06820739e54d38a4a29d6df9155", - "reference": "4ae50d368415a06820739e54d38a4a29d6df9155", + "url": "https://api.github.com/repos/symfony/flex/zipball/9cd384775973eabbf6e8b05784dda279fc67c28d", + "reference": "9cd384775973eabbf6e8b05784dda279fc67c28d", "shasum": "" }, "require": { "composer-plugin-api": "^2.1", - "php": ">=8.0" + "php": ">=8.1" }, "conflict": { - "composer/semver": "<1.7.2" + "composer/semver": "<1.7.2", + "symfony/dotenv": "<5.4" }, "require-dev": { "composer/composer": "^2.1", - "symfony/dotenv": "^5.4|^6.0", - "symfony/filesystem": "^5.4|^6.0", - "symfony/phpunit-bridge": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0" + "symfony/dotenv": "^6.4|^7.4|^8.0", + "symfony/filesystem": "^6.4|^7.4|^8.0", + "symfony/phpunit-bridge": "^6.4|^7.4|^8.0", + "symfony/process": "^6.4|^7.4|^8.0" }, "type": "composer-plugin", "extra": { @@ -7006,7 +7059,7 @@ "description": "Composer plugin for Symfony", "support": { "issues": "https://github.com/symfony/flex/issues", - "source": "https://github.com/symfony/flex/tree/v2.7.1" + "source": "https://github.com/symfony/flex/tree/v2.10.0" }, "funding": [ { @@ -7017,25 +7070,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-05-28T14:22:54+00:00" + "time": "2025-11-16T09:38:19+00:00" }, { "name": "symfony/framework-bundle", - "version": "v6.4.23", + "version": "v6.4.27", "source": { "type": "git", "url": "https://github.com/symfony/framework-bundle.git", - "reference": "ff892d3ab4b8aa35921bc2120a4b31d57948fe22" + "reference": "ee58c2a73218d8f4763824e1414c5f9b4519c91f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/ff892d3ab4b8aa35921bc2120a4b31d57948fe22", - "reference": "ff892d3ab4b8aa35921bc2120a4b31d57948fe22", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/ee58c2a73218d8f4763824e1414c5f9b4519c91f", + "reference": "ee58c2a73218d8f4763824e1414c5f9b4519c91f", "shasum": "" }, "require": { @@ -7155,7 +7212,7 @@ "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/framework-bundle/tree/v6.4.23" + "source": "https://github.com/symfony/framework-bundle/tree/v6.4.27" }, "funding": [ { @@ -7166,25 +7223,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-26T21:24:02+00:00" + "time": "2025-10-15T17:35:09+00:00" }, { "name": "symfony/http-client", - "version": "v6.4.23", + "version": "v6.4.28", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "19f11e742b94dcfd968a54f5381bb9082a88cb57" + "reference": "c9e69c185c4a845f9d46958cdb0dc7aa847f3981" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/19f11e742b94dcfd968a54f5381bb9082a88cb57", - "reference": "19f11e742b94dcfd968a54f5381bb9082a88cb57", + "url": "https://api.github.com/repos/symfony/http-client/zipball/c9e69c185c4a845f9d46958cdb0dc7aa847f3981", + "reference": "c9e69c185c4a845f9d46958cdb0dc7aa847f3981", "shasum": "" }, "require": { @@ -7192,6 +7253,7 @@ "psr/log": "^1|^2|^3", "symfony/deprecation-contracts": "^2.5|^3", "symfony/http-client-contracts": "~3.4.4|^3.5.2", + "symfony/polyfill-php83": "^1.29", "symfony/service-contracts": "^2.5|^3" }, "conflict": { @@ -7248,7 +7310,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v6.4.23" + "source": "https://github.com/symfony/http-client/tree/v6.4.28" }, "funding": [ { @@ -7259,12 +7321,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-27T20:02:31+00:00" + "time": "2025-11-05T17:39:22+00:00" }, { "name": "symfony/http-client-contracts", @@ -7346,16 +7412,16 @@ }, { "name": "symfony/http-foundation", - "version": "v6.4.23", + "version": "v6.4.29", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "452d19f945ee41345fd8a50c18b60783546b7bd3" + "reference": "b03d11e015552a315714c127d8d1e0f9e970ec88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/452d19f945ee41345fd8a50c18b60783546b7bd3", - "reference": "452d19f945ee41345fd8a50c18b60783546b7bd3", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/b03d11e015552a315714c127d8d1e0f9e970ec88", + "reference": "b03d11e015552a315714c127d8d1e0f9e970ec88", "shasum": "" }, "require": { @@ -7403,7 +7469,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v6.4.23" + "source": "https://github.com/symfony/http-foundation/tree/v6.4.29" }, "funding": [ { @@ -7414,25 +7480,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-05-26T09:17:58+00:00" + "time": "2025-11-08T16:40:12+00:00" }, { "name": "symfony/http-kernel", - "version": "v6.4.23", + "version": "v6.4.29", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "2bb2cba685aabd859f22cf6946554e8e7f3c329a" + "reference": "18818b48f54c1d2bd92b41d82d8345af50b15658" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/2bb2cba685aabd859f22cf6946554e8e7f3c329a", - "reference": "2bb2cba685aabd859f22cf6946554e8e7f3c329a", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/18818b48f54c1d2bd92b41d82d8345af50b15658", + "reference": "18818b48f54c1d2bd92b41d82d8345af50b15658", "shasum": "" }, "require": { @@ -7517,7 +7587,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v6.4.23" + "source": "https://github.com/symfony/http-kernel/tree/v6.4.29" }, "funding": [ { @@ -7528,25 +7598,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-28T08:14:51+00:00" + "time": "2025-11-12T11:22:59+00:00" }, { "name": "symfony/mime", - "version": "v6.4.21", + "version": "v6.4.26", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "fec8aa5231f3904754955fad33c2db50594d22d1" + "reference": "61ab9681cdfe315071eb4fa79b6ad6ab030a9235" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/fec8aa5231f3904754955fad33c2db50594d22d1", - "reference": "fec8aa5231f3904754955fad33c2db50594d22d1", + "url": "https://api.github.com/repos/symfony/mime/zipball/61ab9681cdfe315071eb4fa79b6ad6ab030a9235", + "reference": "61ab9681cdfe315071eb4fa79b6ad6ab030a9235", "shasum": "" }, "require": { @@ -7602,7 +7676,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v6.4.21" + "source": "https://github.com/symfony/mime/tree/v6.4.26" }, "funding": [ { @@ -7613,25 +7687,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-27T13:27:38+00:00" + "time": "2025-09-16T08:22:30+00:00" }, { "name": "symfony/monolog-bridge", - "version": "v6.4.13", + "version": "v6.4.28", "source": { "type": "git", "url": "https://github.com/symfony/monolog-bridge.git", - "reference": "9d14621e59f22c2b6d030d92d37ffe5ae1e60452" + "reference": "d2f4b68e3247cf44d93f48545c8c072a75c17e5b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/monolog-bridge/zipball/9d14621e59f22c2b6d030d92d37ffe5ae1e60452", - "reference": "9d14621e59f22c2b6d030d92d37ffe5ae1e60452", + "url": "https://api.github.com/repos/symfony/monolog-bridge/zipball/d2f4b68e3247cf44d93f48545c8c072a75c17e5b", + "reference": "d2f4b68e3247cf44d93f48545c8c072a75c17e5b", "shasum": "" }, "require": { @@ -7681,7 +7759,7 @@ "description": "Provides integration for Monolog with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/monolog-bridge/tree/v6.4.13" + "source": "https://github.com/symfony/monolog-bridge/tree/v6.4.28" }, "funding": [ { @@ -7692,53 +7770,52 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-10-14T08:49:08+00:00" + "time": "2025-10-30T19:57:08+00:00" }, { "name": "symfony/monolog-bundle", - "version": "v3.10.0", + "version": "v3.11.0", "source": { "type": "git", "url": "https://github.com/symfony/monolog-bundle.git", - "reference": "414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181" + "reference": "e12eb92655b234cd50c21cda648088847a7ec777" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181", - "reference": "414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181", + "url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/e12eb92655b234cd50c21cda648088847a7ec777", + "reference": "e12eb92655b234cd50c21cda648088847a7ec777", "shasum": "" }, "require": { + "composer-runtime-api": "^2.0", "monolog/monolog": "^1.25.1 || ^2.0 || ^3.0", - "php": ">=7.2.5", - "symfony/config": "^5.4 || ^6.0 || ^7.0", - "symfony/dependency-injection": "^5.4 || ^6.0 || ^7.0", - "symfony/http-kernel": "^5.4 || ^6.0 || ^7.0", - "symfony/monolog-bridge": "^5.4 || ^6.0 || ^7.0" + "php": ">=8.1", + "symfony/config": "^6.4 || ^7.0", + "symfony/dependency-injection": "^6.4 || ^7.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0", + "symfony/http-kernel": "^6.4 || ^7.0", + "symfony/monolog-bridge": "^6.4 || ^7.0", + "symfony/polyfill-php84": "^1.30" }, "require-dev": { - "symfony/console": "^5.4 || ^6.0 || ^7.0", - "symfony/phpunit-bridge": "^6.3 || ^7.0", - "symfony/yaml": "^5.4 || ^6.0 || ^7.0" + "symfony/console": "^6.4 || ^7.0", + "symfony/phpunit-bridge": "^7.3.3", + "symfony/yaml": "^6.4 || ^7.0" }, "type": "symfony-bundle", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, "autoload": { "psr-4": { - "Symfony\\Bundle\\MonologBundle\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Symfony\\Bundle\\MonologBundle\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -7762,7 +7839,7 @@ ], "support": { "issues": "https://github.com/symfony/monolog-bundle/issues", - "source": "https://github.com/symfony/monolog-bundle/tree/v3.10.0" + "source": "https://github.com/symfony/monolog-bundle/tree/v3.11.0" }, "funding": [ { @@ -7773,25 +7850,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-11-06T17:08:13+00:00" + "time": "2025-11-27T09:16:19+00:00" }, { "name": "symfony/options-resolver", - "version": "v6.4.16", + "version": "v6.4.25", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "368128ad168f20e22c32159b9f761e456cec0c78" + "reference": "d28e7e2db8a73e9511df892d36445f61314bbebe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/368128ad168f20e22c32159b9f761e456cec0c78", - "reference": "368128ad168f20e22c32159b9f761e456cec0c78", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/d28e7e2db8a73e9511df892d36445f61314bbebe", + "reference": "d28e7e2db8a73e9511df892d36445f61314bbebe", "shasum": "" }, "require": { @@ -7829,7 +7910,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v6.4.16" + "source": "https://github.com/symfony/options-resolver/tree/v6.4.25" }, "funding": [ { @@ -7840,25 +7921,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-11-20T10:57:02+00:00" + "time": "2025-08-04T17:06:28+00:00" }, { "name": "symfony/password-hasher", - "version": "v6.4.13", + "version": "v6.4.24", "source": { "type": "git", "url": "https://github.com/symfony/password-hasher.git", - "reference": "e97a1b31f60b8bdfc1fdedab4398538da9441d47" + "reference": "dcab5ac87450aaed26483ba49c2ce86808da7557" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/password-hasher/zipball/e97a1b31f60b8bdfc1fdedab4398538da9441d47", - "reference": "e97a1b31f60b8bdfc1fdedab4398538da9441d47", + "url": "https://api.github.com/repos/symfony/password-hasher/zipball/dcab5ac87450aaed26483ba49c2ce86808da7557", + "reference": "dcab5ac87450aaed26483ba49c2ce86808da7557", "shasum": "" }, "require": { @@ -7901,7 +7986,7 @@ "password" ], "support": { - "source": "https://github.com/symfony/password-hasher/tree/v6.4.13" + "source": "https://github.com/symfony/password-hasher/tree/v6.4.24" }, "funding": [ { @@ -7912,25 +7997,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:18:03+00:00" + "time": "2025-07-10T08:14:14+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", "shasum": "" }, "require": { @@ -7979,7 +8068,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" }, "funding": [ { @@ -7990,16 +8079,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-06-27T09:58:17+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", @@ -8062,7 +8155,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0" }, "funding": [ { @@ -8073,6 +8166,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -8082,7 +8179,7 @@ }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -8143,7 +8240,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" }, "funding": [ { @@ -8154,6 +8251,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -8163,7 +8264,7 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", @@ -8224,7 +8325,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" }, "funding": [ { @@ -8235,6 +8336,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -8312,7 +8417,7 @@ }, { "name": "symfony/polyfill-php80", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", @@ -8372,7 +8477,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" }, "funding": [ { @@ -8383,6 +8488,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -8392,7 +8501,7 @@ }, { "name": "symfony/polyfill-php81", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php81.git", @@ -8448,7 +8557,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.33.0" }, "funding": [ { @@ -8459,6 +8568,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -8468,16 +8581,16 @@ }, { "name": "symfony/polyfill-php83", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491" + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/2fb86d65e2d424369ad2905e83b236a8805ba491", - "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5", "shasum": "" }, "require": { @@ -8524,7 +8637,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0" }, "funding": [ { @@ -8535,25 +8648,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-07-08T02:45:35+00:00" }, { "name": "symfony/polyfill-php84", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php84.git", - "reference": "000df7860439609837bbe28670b0be15783b7fbf" + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/000df7860439609837bbe28670b0be15783b7fbf", - "reference": "000df7860439609837bbe28670b0be15783b7fbf", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", "shasum": "" }, "require": { @@ -8600,7 +8717,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php84/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0" }, "funding": [ { @@ -8611,16 +8728,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-02-20T12:04:08+00:00" + "time": "2025-06-24T13:30:11+00:00" }, { "name": "symfony/polyfill-uuid", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-uuid.git", @@ -8679,7 +8800,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/polyfill-uuid/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.33.0" }, "funding": [ { @@ -8690,6 +8811,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -8699,16 +8824,16 @@ }, { "name": "symfony/process", - "version": "v6.4.20", + "version": "v6.4.26", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "e2a61c16af36c9a07e5c9906498b73e091949a20" + "reference": "48bad913268c8cafabbf7034b39c8bb24fbc5ab8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/e2a61c16af36c9a07e5c9906498b73e091949a20", - "reference": "e2a61c16af36c9a07e5c9906498b73e091949a20", + "url": "https://api.github.com/repos/symfony/process/zipball/48bad913268c8cafabbf7034b39c8bb24fbc5ab8", + "reference": "48bad913268c8cafabbf7034b39c8bb24fbc5ab8", "shasum": "" }, "require": { @@ -8740,7 +8865,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.4.20" + "source": "https://github.com/symfony/process/tree/v6.4.26" }, "funding": [ { @@ -8751,25 +8876,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-03-10T17:11:00+00:00" + "time": "2025-09-11T09:57:09+00:00" }, { "name": "symfony/property-access", - "version": "v6.4.18", + "version": "v6.4.25", "source": { "type": "git", "url": "https://github.com/symfony/property-access.git", - "reference": "80e0378f2f058b60d87dedc3c760caec882e992c" + "reference": "fedc771326d4978a7d3167fa009a509b06a2e168" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-access/zipball/80e0378f2f058b60d87dedc3c760caec882e992c", - "reference": "80e0378f2f058b60d87dedc3c760caec882e992c", + "url": "https://api.github.com/repos/symfony/property-access/zipball/fedc771326d4978a7d3167fa009a509b06a2e168", + "reference": "fedc771326d4978a7d3167fa009a509b06a2e168", "shasum": "" }, "require": { @@ -8817,7 +8946,7 @@ "reflection" ], "support": { - "source": "https://github.com/symfony/property-access/tree/v6.4.18" + "source": "https://github.com/symfony/property-access/tree/v6.4.25" }, "funding": [ { @@ -8828,25 +8957,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-12-16T14:42:05+00:00" + "time": "2025-08-12T15:42:57+00:00" }, { "name": "symfony/property-info", - "version": "v6.4.18", + "version": "v6.4.24", "source": { "type": "git", "url": "https://github.com/symfony/property-info.git", - "reference": "94d18e5cc11a37fd92856d38b61d9cdf72536a1e" + "reference": "1056ae3621eeddd78d7c5ec074f1c1784324eec6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-info/zipball/94d18e5cc11a37fd92856d38b61d9cdf72536a1e", - "reference": "94d18e5cc11a37fd92856d38b61d9cdf72536a1e", + "url": "https://api.github.com/repos/symfony/property-info/zipball/1056ae3621eeddd78d7c5ec074f1c1784324eec6", + "reference": "1056ae3621eeddd78d7c5ec074f1c1784324eec6", "shasum": "" }, "require": { @@ -8903,7 +9036,7 @@ "validator" ], "support": { - "source": "https://github.com/symfony/property-info/tree/v6.4.18" + "source": "https://github.com/symfony/property-info/tree/v6.4.24" }, "funding": [ { @@ -8914,25 +9047,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-21T10:52:27+00:00" + "time": "2025-07-14T16:38:25+00:00" }, { "name": "symfony/proxy-manager-bridge", - "version": "v6.4.13", + "version": "v6.4.28", "source": { "type": "git", "url": "https://github.com/symfony/proxy-manager-bridge.git", - "reference": "8932b572e147e80fb498045c580eb14215197529" + "reference": "9ecac7f98ad685d474394dbd06dab29bab4e18a6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/proxy-manager-bridge/zipball/8932b572e147e80fb498045c580eb14215197529", - "reference": "8932b572e147e80fb498045c580eb14215197529", + "url": "https://api.github.com/repos/symfony/proxy-manager-bridge/zipball/9ecac7f98ad685d474394dbd06dab29bab4e18a6", + "reference": "9ecac7f98ad685d474394dbd06dab29bab4e18a6", "shasum": "" }, "require": { @@ -8970,7 +9107,7 @@ "description": "Provides integration for ProxyManager with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/proxy-manager-bridge/tree/v6.4.13" + "source": "https://github.com/symfony/proxy-manager-bridge/tree/v6.4.28" }, "funding": [ { @@ -8981,25 +9118,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:18:03+00:00" + "time": "2025-11-02T18:11:54+00:00" }, { "name": "symfony/routing", - "version": "v6.4.22", + "version": "v6.4.28", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "1f5234e8457164a3a0038a4c0a4ba27876a9c670" + "reference": "ae064a6d9cf39507f9797658465a2ca702965fa8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/1f5234e8457164a3a0038a4c0a4ba27876a9c670", - "reference": "1f5234e8457164a3a0038a4c0a4ba27876a9c670", + "url": "https://api.github.com/repos/symfony/routing/zipball/ae064a6d9cf39507f9797658465a2ca702965fa8", + "reference": "ae064a6d9cf39507f9797658465a2ca702965fa8", "shasum": "" }, "require": { @@ -9053,7 +9194,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v6.4.22" + "source": "https://github.com/symfony/routing/tree/v6.4.28" }, "funding": [ { @@ -9064,25 +9205,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-27T16:08:38+00:00" + "time": "2025-10-31T16:43:05+00:00" }, { "name": "symfony/runtime", - "version": "v6.4.23", + "version": "v6.4.26", "source": { "type": "git", "url": "https://github.com/symfony/runtime.git", - "reference": "ef1f03c2ab1144ac4ef7744b9e026bdb06f2f88f" + "reference": "59933ca737fd60fad548241b6d879cd0e4be31ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/runtime/zipball/ef1f03c2ab1144ac4ef7744b9e026bdb06f2f88f", - "reference": "ef1f03c2ab1144ac4ef7744b9e026bdb06f2f88f", + "url": "https://api.github.com/repos/symfony/runtime/zipball/59933ca737fd60fad548241b6d879cd0e4be31ab", + "reference": "59933ca737fd60fad548241b6d879cd0e4be31ab", "shasum": "" }, "require": { @@ -9132,7 +9277,7 @@ "runtime" ], "support": { - "source": "https://github.com/symfony/runtime/tree/v6.4.23" + "source": "https://github.com/symfony/runtime/tree/v6.4.26" }, "funding": [ { @@ -9143,25 +9288,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-13T07:44:14+00:00" + "time": "2025-09-11T15:30:54+00:00" }, { "name": "symfony/security-bundle", - "version": "v6.4.23", + "version": "v6.4.26", "source": { "type": "git", "url": "https://github.com/symfony/security-bundle.git", - "reference": "3db1460f539b23e74a119981ea6b3002302250bc" + "reference": "b83773107a5b83a5507df9e88bd50d495f6e8b72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-bundle/zipball/3db1460f539b23e74a119981ea6b3002302250bc", - "reference": "3db1460f539b23e74a119981ea6b3002302250bc", + "url": "https://api.github.com/repos/symfony/security-bundle/zipball/b83773107a5b83a5507df9e88bd50d495f6e8b72", + "reference": "b83773107a5b83a5507df9e88bd50d495f6e8b72", "shasum": "" }, "require": { @@ -9244,7 +9393,7 @@ "description": "Provides a tight integration of the Security component into the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-bundle/tree/v6.4.23" + "source": "https://github.com/symfony/security-bundle/tree/v6.4.26" }, "funding": [ { @@ -9255,25 +9404,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-23T20:18:57+00:00" + "time": "2025-09-22T15:03:07+00:00" }, { "name": "symfony/security-core", - "version": "v6.4.23", + "version": "v6.4.27", "source": { "type": "git", "url": "https://github.com/symfony/security-core.git", - "reference": "527780a0482e592530174ca90e6189f64cdf6569" + "reference": "673018434b38e504eb04ca3c6d7e2e7c86735bfb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-core/zipball/527780a0482e592530174ca90e6189f64cdf6569", - "reference": "527780a0482e592530174ca90e6189f64cdf6569", + "url": "https://api.github.com/repos/symfony/security-core/zipball/673018434b38e504eb04ca3c6d7e2e7c86735bfb", + "reference": "673018434b38e504eb04ca3c6d7e2e7c86735bfb", "shasum": "" }, "require": { @@ -9330,7 +9483,7 @@ "description": "Symfony Security Component - Core Library", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-core/tree/v6.4.23" + "source": "https://github.com/symfony/security-core/tree/v6.4.27" }, "funding": [ { @@ -9341,25 +9494,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-05-30T08:33:44+00:00" + "time": "2025-10-23T19:49:35+00:00" }, { "name": "symfony/security-csrf", - "version": "v6.4.13", + "version": "v6.4.24", "source": { "type": "git", "url": "https://github.com/symfony/security-csrf.git", - "reference": "c34421b7d34efbaef5d611ab2e646a0ec464ffe3" + "reference": "9a1efc8c10b86bcedc9233affd10c716b54ca1b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-csrf/zipball/c34421b7d34efbaef5d611ab2e646a0ec464ffe3", - "reference": "c34421b7d34efbaef5d611ab2e646a0ec464ffe3", + "url": "https://api.github.com/repos/symfony/security-csrf/zipball/9a1efc8c10b86bcedc9233affd10c716b54ca1b7", + "reference": "9a1efc8c10b86bcedc9233affd10c716b54ca1b7", "shasum": "" }, "require": { @@ -9398,7 +9555,7 @@ "description": "Symfony Security Component - CSRF Library", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-csrf/tree/v6.4.13" + "source": "https://github.com/symfony/security-csrf/tree/v6.4.24" }, "funding": [ { @@ -9409,25 +9566,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:18:03+00:00" + "time": "2025-07-10T08:14:14+00:00" }, { "name": "symfony/security-http", - "version": "v6.4.23", + "version": "v6.4.26", "source": { "type": "git", "url": "https://github.com/symfony/security-http.git", - "reference": "815fcda8122a7850bf6d5d842ce03c20445295bb" + "reference": "6c2e236f0fc3e0853770a5574ef7af471486ba4c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-http/zipball/815fcda8122a7850bf6d5d842ce03c20445295bb", - "reference": "815fcda8122a7850bf6d5d842ce03c20445295bb", + "url": "https://api.github.com/repos/symfony/security-http/zipball/6c2e236f0fc3e0853770a5574ef7af471486ba4c", + "reference": "6c2e236f0fc3e0853770a5574ef7af471486ba4c", "shasum": "" }, "require": { @@ -9486,7 +9647,7 @@ "description": "Symfony Security Component - HTTP Integration", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-http/tree/v6.4.23" + "source": "https://github.com/symfony/security-http/tree/v6.4.26" }, "funding": [ { @@ -9497,25 +9658,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-23T20:18:57+00:00" + "time": "2025-09-05T18:17:25+00:00" }, { "name": "symfony/serializer", - "version": "v6.4.23", + "version": "v6.4.27", "source": { "type": "git", "url": "https://github.com/symfony/serializer.git", - "reference": "b40a697a2bb2c3d841a1f9e34a8a9f50bf9d1d06" + "reference": "28779bbdb398cac3421d0e51f7ca669e4a27c5ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/serializer/zipball/b40a697a2bb2c3d841a1f9e34a8a9f50bf9d1d06", - "reference": "b40a697a2bb2c3d841a1f9e34a8a9f50bf9d1d06", + "url": "https://api.github.com/repos/symfony/serializer/zipball/28779bbdb398cac3421d0e51f7ca669e4a27c5ac", + "reference": "28779bbdb398cac3421d0e51f7ca669e4a27c5ac", "shasum": "" }, "require": { @@ -9584,7 +9749,7 @@ "description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/serializer/tree/v6.4.23" + "source": "https://github.com/symfony/serializer/tree/v6.4.27" }, "funding": [ { @@ -9595,25 +9760,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-27T15:34:20+00:00" + "time": "2025-10-08T04:24:22+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.6.0", + "version": "v3.6.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", "shasum": "" }, "require": { @@ -9667,7 +9836,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" }, "funding": [ { @@ -9678,25 +9847,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-25T09:37:31+00:00" + "time": "2025-07-15T11:30:57+00:00" }, { "name": "symfony/stopwatch", - "version": "v6.4.19", + "version": "v6.4.24", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "dfe1481c12c06266d0c3d58c0cb4b09bd497ab9c" + "reference": "b67e94e06a05d9572c2fa354483b3e13e3cb1898" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/dfe1481c12c06266d0c3d58c0cb4b09bd497ab9c", - "reference": "dfe1481c12c06266d0c3d58c0cb4b09bd497ab9c", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/b67e94e06a05d9572c2fa354483b3e13e3cb1898", + "reference": "b67e94e06a05d9572c2fa354483b3e13e3cb1898", "shasum": "" }, "require": { @@ -9729,7 +9902,7 @@ "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v6.4.19" + "source": "https://github.com/symfony/stopwatch/tree/v6.4.24" }, "funding": [ { @@ -9740,25 +9913,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-02-21T10:06:30+00:00" + "time": "2025-07-10T08:14:14+00:00" }, { "name": "symfony/string", - "version": "v6.4.21", + "version": "v6.4.26", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "73e2c6966a5aef1d4892873ed5322245295370c6" + "reference": "5621f039a71a11c87c106c1c598bdcd04a19aeea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/73e2c6966a5aef1d4892873ed5322245295370c6", - "reference": "73e2c6966a5aef1d4892873ed5322245295370c6", + "url": "https://api.github.com/repos/symfony/string/zipball/5621f039a71a11c87c106c1c598bdcd04a19aeea", + "reference": "5621f039a71a11c87c106c1c598bdcd04a19aeea", "shasum": "" }, "require": { @@ -9772,7 +9949,6 @@ "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^5.4|^6.0|^7.0", "symfony/http-client": "^5.4|^6.0|^7.0", "symfony/intl": "^6.2|^7.0", "symfony/translation-contracts": "^2.5|^3.0", @@ -9815,7 +9991,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.4.21" + "source": "https://github.com/symfony/string/tree/v6.4.26" }, "funding": [ { @@ -9826,25 +10002,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-04-18T15:23:29+00:00" + "time": "2025-09-11T14:32:46+00:00" }, { "name": "symfony/translation-contracts", - "version": "v3.6.0", + "version": "v3.6.1", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d" + "reference": "65a8bc82080447fae78373aa10f8d13b38338977" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/df210c7a2573f1913b2d17cc95f90f53a73d8f7d", - "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/65a8bc82080447fae78373aa10f8d13b38338977", + "reference": "65a8bc82080447fae78373aa10f8d13b38338977", "shasum": "" }, "require": { @@ -9893,7 +10073,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/translation-contracts/tree/v3.6.1" }, "funding": [ { @@ -9904,25 +10084,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-27T08:32:26+00:00" + "time": "2025-07-15T13:41:35+00:00" }, { "name": "symfony/twig-bridge", - "version": "v6.4.22", + "version": "v6.4.25", "source": { "type": "git", "url": "https://github.com/symfony/twig-bridge.git", - "reference": "04ab306a2f2c9dbd46f4363383812954f704af9d" + "reference": "9d13e87591c9de3221c8d6f23cd9a2b5958607bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/04ab306a2f2c9dbd46f4363383812954f704af9d", - "reference": "04ab306a2f2c9dbd46f4363383812954f704af9d", + "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/9d13e87591c9de3221c8d6f23cd9a2b5958607bf", + "reference": "9d13e87591c9de3221c8d6f23cd9a2b5958607bf", "shasum": "" }, "require": { @@ -10002,7 +10186,7 @@ "description": "Provides integration for Twig with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/twig-bridge/tree/v6.4.22" + "source": "https://github.com/symfony/twig-bridge/tree/v6.4.25" }, "funding": [ { @@ -10013,25 +10197,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-05-16T08:23:44+00:00" + "time": "2025-08-13T09:41:44+00:00" }, { "name": "symfony/twig-bundle", - "version": "v6.4.23", + "version": "v6.4.24", "source": { "type": "git", "url": "https://github.com/symfony/twig-bundle.git", - "reference": "ef970ed7eb9e547d21628e4c803de0943759cbcd" + "reference": "3b48b6e8225495c6d2438828982b4d219ca565ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/ef970ed7eb9e547d21628e4c803de0943759cbcd", - "reference": "ef970ed7eb9e547d21628e4c803de0943759cbcd", + "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/3b48b6e8225495c6d2438828982b4d219ca565ba", + "reference": "3b48b6e8225495c6d2438828982b4d219ca565ba", "shasum": "" }, "require": { @@ -10086,7 +10274,7 @@ "description": "Provides a tight integration of Twig into the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/twig-bundle/tree/v6.4.23" + "source": "https://github.com/symfony/twig-bundle/tree/v6.4.24" }, "funding": [ { @@ -10097,25 +10285,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-20T20:02:07+00:00" + "time": "2025-07-10T08:14:14+00:00" }, { "name": "symfony/uid", - "version": "v6.4.23", + "version": "v6.4.24", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "9c8592da78d7ee6af52011eef593350d87e814c0" + "reference": "17da16a750541a42cf2183935e0f6008316c23f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/9c8592da78d7ee6af52011eef593350d87e814c0", - "reference": "9c8592da78d7ee6af52011eef593350d87e814c0", + "url": "https://api.github.com/repos/symfony/uid/zipball/17da16a750541a42cf2183935e0f6008316c23f7", + "reference": "17da16a750541a42cf2183935e0f6008316c23f7", "shasum": "" }, "require": { @@ -10160,7 +10352,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v6.4.23" + "source": "https://github.com/symfony/uid/tree/v6.4.24" }, "funding": [ { @@ -10171,25 +10363,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-26T08:06:12+00:00" + "time": "2025-07-10T08:14:14+00:00" }, { "name": "symfony/validator", - "version": "v6.4.23", + "version": "v6.4.29", "source": { "type": "git", "url": "https://github.com/symfony/validator.git", - "reference": "6506760ab57e7cda5bde9cdaed736526162284bc" + "reference": "99df8a769e64e399f510166141ea74f450e8dd1d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/validator/zipball/6506760ab57e7cda5bde9cdaed736526162284bc", - "reference": "6506760ab57e7cda5bde9cdaed736526162284bc", + "url": "https://api.github.com/repos/symfony/validator/zipball/99df8a769e64e399f510166141ea74f450e8dd1d", + "reference": "99df8a769e64e399f510166141ea74f450e8dd1d", "shasum": "" }, "require": { @@ -10257,7 +10453,7 @@ "description": "Provides tools to validate values", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/validator/tree/v6.4.23" + "source": "https://github.com/symfony/validator/tree/v6.4.29" }, "funding": [ { @@ -10268,25 +10464,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-26T07:25:45+00:00" + "time": "2025-11-06T20:26:06+00:00" }, { "name": "symfony/var-dumper", - "version": "v6.4.23", + "version": "v6.4.26", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "d55b1834cdbfcc31bc2cd7e095ba5ed9a88f6600" + "reference": "cfae1497a2f1eaad78dbc0590311c599c7178d4a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/d55b1834cdbfcc31bc2cd7e095ba5ed9a88f6600", - "reference": "d55b1834cdbfcc31bc2cd7e095ba5ed9a88f6600", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/cfae1497a2f1eaad78dbc0590311c599c7178d4a", + "reference": "cfae1497a2f1eaad78dbc0590311c599c7178d4a", "shasum": "" }, "require": { @@ -10298,7 +10498,6 @@ "symfony/console": "<5.4" }, "require-dev": { - "ext-iconv": "*", "symfony/console": "^5.4|^6.0|^7.0", "symfony/error-handler": "^6.3|^7.0", "symfony/http-kernel": "^5.4|^6.0|^7.0", @@ -10342,7 +10541,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.4.23" + "source": "https://github.com/symfony/var-dumper/tree/v6.4.26" }, "funding": [ { @@ -10353,25 +10552,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-27T15:05:27+00:00" + "time": "2025-09-25T15:37:27+00:00" }, { "name": "symfony/var-exporter", - "version": "v6.4.22", + "version": "v6.4.26", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "f28cf841f5654955c9f88ceaf4b9dc29571988a9" + "reference": "466fcac5fa2e871f83d31173f80e9c2684743bfc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/f28cf841f5654955c9f88ceaf4b9dc29571988a9", - "reference": "f28cf841f5654955c9f88ceaf4b9dc29571988a9", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/466fcac5fa2e871f83d31173f80e9c2684743bfc", + "reference": "466fcac5fa2e871f83d31173f80e9c2684743bfc", "shasum": "" }, "require": { @@ -10419,7 +10622,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v6.4.22" + "source": "https://github.com/symfony/var-exporter/tree/v6.4.26" }, "funding": [ { @@ -10430,25 +10633,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-05-14T13:00:13+00:00" + "time": "2025-09-11T09:57:09+00:00" }, { "name": "symfony/web-link", - "version": "v6.4.22", + "version": "v6.4.24", "source": { "type": "git", "url": "https://github.com/symfony/web-link.git", - "reference": "8595204221c4307b5fd30644a225b0b952082b18" + "reference": "75ffbb304f26a716969863328c8c6a11eadcfa5a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/web-link/zipball/8595204221c4307b5fd30644a225b0b952082b18", - "reference": "8595204221c4307b5fd30644a225b0b952082b18", + "url": "https://api.github.com/repos/symfony/web-link/zipball/75ffbb304f26a716969863328c8c6a11eadcfa5a", + "reference": "75ffbb304f26a716969863328c8c6a11eadcfa5a", "shasum": "" }, "require": { @@ -10502,7 +10709,7 @@ "push" ], "support": { - "source": "https://github.com/symfony/web-link/tree/v6.4.22" + "source": "https://github.com/symfony/web-link/tree/v6.4.24" }, "funding": [ { @@ -10513,25 +10720,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-05-16T08:23:44+00:00" + "time": "2025-07-10T08:14:14+00:00" }, { "name": "symfony/yaml", - "version": "v6.4.23", + "version": "v6.4.26", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "93e29e0deb5f1b2e360adfb389a20d25eb81a27b" + "reference": "0fc8b966fd0dcaab544ae59bfc3a433f048c17b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/93e29e0deb5f1b2e360adfb389a20d25eb81a27b", - "reference": "93e29e0deb5f1b2e360adfb389a20d25eb81a27b", + "url": "https://api.github.com/repos/symfony/yaml/zipball/0fc8b966fd0dcaab544ae59bfc3a433f048c17b0", + "reference": "0fc8b966fd0dcaab544ae59bfc3a433f048c17b0", "shasum": "" }, "require": { @@ -10574,7 +10785,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v6.4.23" + "source": "https://github.com/symfony/yaml/tree/v6.4.26" }, "funding": [ { @@ -10585,25 +10796,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-03T06:46:12+00:00" + "time": "2025-09-26T15:07:38+00:00" }, { "name": "twig/twig", - "version": "v3.21.1", + "version": "v3.22.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "285123877d4dd97dd7c11842ac5fb7e86e60d81d" + "reference": "4509984193026de413baf4ba80f68590a7f2c51d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/285123877d4dd97dd7c11842ac5fb7e86e60d81d", - "reference": "285123877d4dd97dd7c11842ac5fb7e86e60d81d", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/4509984193026de413baf4ba80f68590a7f2c51d", + "reference": "4509984193026de413baf4ba80f68590a7f2c51d", "shasum": "" }, "require": { @@ -10657,7 +10872,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.21.1" + "source": "https://github.com/twigphp/Twig/tree/v3.22.0" }, "funding": [ { @@ -10669,20 +10884,20 @@ "type": "tidelift" } ], - "time": "2025-05-03T07:21:55+00:00" + "time": "2025-10-29T15:56:47+00:00" }, { "name": "vich/uploader-bundle", - "version": "v2.7.0", + "version": "v2.8.1", "source": { "type": "git", "url": "https://github.com/dustin10/VichUploaderBundle.git", - "reference": "8a8cce134fc686caadf6828956f8ddd2b383fe74" + "reference": "0619f0831003a16c65c6e038833430318705cbe1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dustin10/VichUploaderBundle/zipball/8a8cce134fc686caadf6828956f8ddd2b383fe74", - "reference": "8a8cce134fc686caadf6828956f8ddd2b383fe74", + "url": "https://api.github.com/repos/dustin10/VichUploaderBundle/zipball/0619f0831003a16c65c6e038833430318705cbe1", + "reference": "0619f0831003a16c65c6e038833430318705cbe1", "shasum": "" }, "require": { @@ -10775,34 +10990,34 @@ ], "support": { "issues": "https://github.com/dustin10/VichUploaderBundle/issues", - "source": "https://github.com/dustin10/VichUploaderBundle/tree/v2.7.0" + "source": "https://github.com/dustin10/VichUploaderBundle/tree/v2.8.1" }, - "time": "2025-06-12T06:59:34+00:00" + "time": "2025-09-12T07:17:19+00:00" }, { "name": "webmozart/assert", - "version": "1.11.0", + "version": "1.12.1", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68", + "reference": "9be6926d8b485f55b9229203f962b51ed377ba68", "shasum": "" }, "require": { "ext-ctype": "*", + "ext-date": "*", + "ext-filter": "*", "php": "^7.2 || ^8.0" }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" }, "type": "library", "extra": { @@ -10833,9 +11048,9 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" + "source": "https://github.com/webmozarts/assert/tree/1.12.1" }, - "time": "2022-06-03T18:03:27+00:00" + "time": "2025-10-29T15:56:20+00:00" }, { "name": "willdurand/negotiation", @@ -10897,43 +11112,36 @@ "packages-dev": [ { "name": "amphp/amp", - "version": "v2.6.4", + "version": "v3.1.1", "source": { "type": "git", "url": "https://github.com/amphp/amp.git", - "reference": "ded3d9be08f526089eb7ee8d9f16a9768f9dec2d" + "reference": "fa0ab33a6f47a82929c38d03ca47ebb71086a93f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amphp/amp/zipball/ded3d9be08f526089eb7ee8d9f16a9768f9dec2d", - "reference": "ded3d9be08f526089eb7ee8d9f16a9768f9dec2d", + "url": "https://api.github.com/repos/amphp/amp/zipball/fa0ab33a6f47a82929c38d03ca47ebb71086a93f", + "reference": "fa0ab33a6f47a82929c38d03ca47ebb71086a93f", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" }, "require-dev": { - "amphp/php-cs-fixer-config": "dev-master", - "amphp/phpunit-util": "^1", - "ext-json": "*", - "jetbrains/phpstorm-stubs": "^2019.3", - "phpunit/phpunit": "^7 | ^8 | ^9", - "react/promise": "^2", - "vimeo/psalm": "^3.12" + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "5.23.1" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - } - }, "autoload": { "files": [ - "lib/functions.php", - "lib/Internal/functions.php" + "src/functions.php", + "src/Future/functions.php", + "src/Internal/functions.php" ], "psr-4": { - "Amp\\": "lib" + "Amp\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -10941,10 +11149,6 @@ "MIT" ], "authors": [ - { - "name": "Daniel Lowrey", - "email": "rdlowrey@php.net" - }, { "name": "Aaron Piotrowski", "email": "aaron@trowski.com" @@ -10956,6 +11160,10 @@ { "name": "Niklas Keller", "email": "me@kelunik.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" } ], "description": "A non-blocking concurrency framework for PHP applications.", @@ -10972,9 +11180,8 @@ "promise" ], "support": { - "irc": "irc://irc.freenode.org/amphp", "issues": "https://github.com/amphp/amp/issues", - "source": "https://github.com/amphp/amp/tree/v2.6.4" + "source": "https://github.com/amphp/amp/tree/v3.1.1" }, "funding": [ { @@ -10982,41 +11189,45 @@ "type": "github" } ], - "time": "2024-03-21T18:52:26+00:00" + "time": "2025-08-27T21:42:00+00:00" }, { "name": "amphp/byte-stream", - "version": "v1.8.2", + "version": "v2.1.2", "source": { "type": "git", "url": "https://github.com/amphp/byte-stream.git", - "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc" + "reference": "55a6bd071aec26fa2a3e002618c20c35e3df1b46" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amphp/byte-stream/zipball/4f0e968ba3798a423730f567b1b50d3441c16ddc", - "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/55a6bd071aec26fa2a3e002618c20c35e3df1b46", + "reference": "55a6bd071aec26fa2a3e002618c20c35e3df1b46", "shasum": "" }, "require": { - "amphp/amp": "^2", - "php": ">=7.1" + "amphp/amp": "^3", + "amphp/parser": "^1.1", + "amphp/pipeline": "^1", + "amphp/serialization": "^1", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2.3" }, "require-dev": { - "amphp/php-cs-fixer-config": "dev-master", - "amphp/phpunit-util": "^1.4", - "friendsofphp/php-cs-fixer": "^2.3", - "jetbrains/phpstorm-stubs": "^2019.3", - "phpunit/phpunit": "^6 || ^7 || ^8", - "psalm/phar": "^3.11.4" + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.22.1" }, "type": "library", "autoload": { "files": [ - "lib/functions.php" + "src/functions.php", + "src/Internal/functions.php" ], "psr-4": { - "Amp\\ByteStream\\": "lib" + "Amp\\ByteStream\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -11045,7 +11256,7 @@ ], "support": { "issues": "https://github.com/amphp/byte-stream/issues", - "source": "https://github.com/amphp/byte-stream/tree/v1.8.2" + "source": "https://github.com/amphp/byte-stream/tree/v2.1.2" }, "funding": [ { @@ -11053,94 +11264,112 @@ "type": "github" } ], - "time": "2024-04-13T18:00:56+00:00" + "time": "2025-03-16T17:10:27+00:00" }, { - "name": "dnoegel/php-xdg-base-dir", - "version": "v0.1.1", + "name": "amphp/cache", + "version": "v2.0.1", "source": { "type": "git", - "url": "https://github.com/dnoegel/php-xdg-base-dir.git", - "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd" + "url": "https://github.com/amphp/cache.git", + "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", - "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "url": "https://api.github.com/repos/amphp/cache/zipball/46912e387e6aa94933b61ea1ead9cf7540b7797c", + "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c", "shasum": "" }, "require": { - "php": ">=5.3.2" + "amphp/amp": "^3", + "amphp/serialization": "^1", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" }, "require-dev": { - "phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35" + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" }, "type": "library", "autoload": { "psr-4": { - "XdgBaseDir\\": "src/" + "Amp\\Cache\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "implementation of xdg base directory specification for php", + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + } + ], + "description": "A fiber-aware cache API based on Amp and Revolt.", + "homepage": "https://amphp.org/cache", "support": { - "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues", - "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1" + "issues": "https://github.com/amphp/cache/issues", + "source": "https://github.com/amphp/cache/tree/v2.0.1" }, - "time": "2019-12-04T15:06:13+00:00" + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-04-19T03:38:06+00:00" }, { - "name": "doctrine/data-fixtures", - "version": "1.8.2", + "name": "amphp/dns", + "version": "v2.4.0", "source": { "type": "git", - "url": "https://github.com/doctrine/data-fixtures.git", - "reference": "6fb221da56dae2011b33d47508e3b8aeb1d91db5" + "url": "https://github.com/amphp/dns.git", + "reference": "78eb3db5fc69bf2fc0cb503c4fcba667bc223c71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/data-fixtures/zipball/6fb221da56dae2011b33d47508e3b8aeb1d91db5", - "reference": "6fb221da56dae2011b33d47508e3b8aeb1d91db5", + "url": "https://api.github.com/repos/amphp/dns/zipball/78eb3db5fc69bf2fc0cb503c4fcba667bc223c71", + "reference": "78eb3db5fc69bf2fc0cb503c4fcba667bc223c71", "shasum": "" }, "require": { - "doctrine/deprecations": "^0.5.3 || ^1.0", - "doctrine/persistence": "^2.0 || ^3.0", - "php": "^7.4 || ^8.0", - "symfony/polyfill-php80": "^1" - }, - "conflict": { - "doctrine/dbal": "<3.5 || >=5", - "doctrine/orm": "<2.14 || >=4", - "doctrine/phpcr-odm": "<1.3.0" + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/cache": "^2", + "amphp/parser": "^1", + "amphp/process": "^2", + "daverandom/libdns": "^2.0.2", + "ext-filter": "*", + "ext-json": "*", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" }, "require-dev": { - "doctrine/annotations": "^1.12 || ^2", - "doctrine/coding-standard": "^13", - "doctrine/dbal": "^3.5 || ^4", - "doctrine/mongodb-odm": "^1.3.0 || ^2.0.0", - "doctrine/orm": "^2.14 || ^3", - "ext-sqlite3": "*", - "fig/log-test": "^1", - "phpstan/phpstan": "2.1.17", - "phpunit/phpunit": "^9.6.13 || 10.5.45", - "psr/log": "^1.1 || ^2 || ^3", - "symfony/cache": "^5.4 || ^6.3 || ^7", - "symfony/var-exporter": "^5.4 || ^6.3 || ^7" - }, - "suggest": { - "alcaeus/mongo-php-adapter": "For using MongoDB ODM 1.3 with PHP 7 (deprecated)", - "doctrine/mongodb-odm": "For loading MongoDB ODM fixtures", - "doctrine/orm": "For loading ORM fixtures", - "doctrine/phpcr-odm": "For loading PHPCR ODM fixtures" + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.20" }, "type": "library", "autoload": { + "files": [ + "src/functions.php" + ], "psr-4": { - "Doctrine\\Common\\DataFixtures\\": "src" + "Amp\\Dns\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -11149,90 +11378,91 @@ ], "authors": [ { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" + "name": "Chris Wright", + "email": "addr@daverandom.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" } ], - "description": "Data Fixtures for all Doctrine Object Managers", - "homepage": "https://www.doctrine-project.org", + "description": "Async DNS resolution for Amp.", + "homepage": "https://github.com/amphp/dns", "keywords": [ - "database" + "amp", + "amphp", + "async", + "client", + "dns", + "resolve" ], "support": { - "issues": "https://github.com/doctrine/data-fixtures/issues", - "source": "https://github.com/doctrine/data-fixtures/tree/1.8.2" + "issues": "https://github.com/amphp/dns/issues", + "source": "https://github.com/amphp/dns/tree/v2.4.0" }, "funding": [ { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdata-fixtures", - "type": "tidelift" + "url": "https://github.com/amphp", + "type": "github" } ], - "time": "2025-06-10T07:00:05+00:00" + "time": "2025-01-19T15:43:40+00:00" }, { - "name": "ergebnis/composer-normalize", - "version": "2.47.0", + "name": "amphp/parallel", + "version": "v2.3.2", "source": { "type": "git", - "url": "https://github.com/ergebnis/composer-normalize.git", - "reference": "ed24b9f8901f8fbafeca98f662eaca39427f0544" + "url": "https://github.com/amphp/parallel.git", + "reference": "321b45ae771d9c33a068186b24117e3cd1c48dce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ergebnis/composer-normalize/zipball/ed24b9f8901f8fbafeca98f662eaca39427f0544", - "reference": "ed24b9f8901f8fbafeca98f662eaca39427f0544", + "url": "https://api.github.com/repos/amphp/parallel/zipball/321b45ae771d9c33a068186b24117e3cd1c48dce", + "reference": "321b45ae771d9c33a068186b24117e3cd1c48dce", "shasum": "" }, "require": { - "composer-plugin-api": "^2.0.0", - "ergebnis/json": "^1.4.0", - "ergebnis/json-normalizer": "^4.9.0", - "ergebnis/json-printer": "^3.7.0", - "ext-json": "*", - "justinrainbow/json-schema": "^5.2.12 || ^6.0.0", - "localheinz/diff": "^1.2.0", - "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/cache": "^2", + "amphp/parser": "^1", + "amphp/pipeline": "^1", + "amphp/process": "^2", + "amphp/serialization": "^1", + "amphp/socket": "^2", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1" }, "require-dev": { - "composer/composer": "^2.8.3", - "ergebnis/license": "^2.6.0", - "ergebnis/php-cs-fixer-config": "^6.46.0", - "ergebnis/phpunit-slow-test-detector": "^2.19.1", - "fakerphp/faker": "^1.24.1", - "infection/infection": "~0.26.6", - "phpstan/extension-installer": "^1.4.3", - "phpstan/phpstan": "^2.1.11", - "phpstan/phpstan-deprecation-rules": "^2.0.1", - "phpstan/phpstan-phpunit": "^2.0.6", - "phpstan/phpstan-strict-rules": "^2.0.4", - "phpunit/phpunit": "^9.6.20", - "rector/rector": "^2.0.11", - "symfony/filesystem": "^5.4.41" - }, - "type": "composer-plugin", - "extra": { - "class": "Ergebnis\\Composer\\Normalize\\NormalizePlugin", - "branch-alias": { - "dev-main": "2.44-dev" - }, - "plugin-optional": true, - "composer-normalize": { - "indent-size": 2, - "indent-style": "space" - } + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.18" }, + "type": "library", "autoload": { + "files": [ + "src/Context/functions.php", + "src/Context/Internal/functions.php", + "src/Ipc/functions.php", + "src/Worker/functions.php" + ], "psr-4": { - "Ergebnis\\Composer\\Normalize\\": "src/" + "Amp\\Parallel\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -11241,61 +11471,796 @@ ], "authors": [ { - "name": "Andreas Möller", - "email": "am@localheinz.com", - "homepage": "https://localheinz.com" + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Stephen Coakley", + "email": "me@stephencoakley.com" } ], - "description": "Provides a composer plugin for normalizing composer.json.", - "homepage": "https://github.com/ergebnis/composer-normalize", + "description": "Parallel processing component for Amp.", + "homepage": "https://github.com/amphp/parallel", "keywords": [ - "composer", - "normalize", - "normalizer", - "plugin" + "async", + "asynchronous", + "concurrent", + "multi-processing", + "multi-threading" ], "support": { - "issues": "https://github.com/ergebnis/composer-normalize/issues", + "issues": "https://github.com/amphp/parallel/issues", + "source": "https://github.com/amphp/parallel/tree/v2.3.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2025-08-27T21:55:40+00:00" + }, + { + "name": "amphp/parser", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/parser.git", + "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/parser/zipball/3cf1f8b32a0171d4b1bed93d25617637a77cded7", + "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Parser\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A generator parser to make streaming parsers simple.", + "homepage": "https://github.com/amphp/parser", + "keywords": [ + "async", + "non-blocking", + "parser", + "stream" + ], + "support": { + "issues": "https://github.com/amphp/parser/issues", + "source": "https://github.com/amphp/parser/tree/v1.1.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-03-21T19:16:53+00:00" + }, + { + "name": "amphp/pipeline", + "version": "v1.2.3", + "source": { + "type": "git", + "url": "https://github.com/amphp/pipeline.git", + "reference": "7b52598c2e9105ebcddf247fc523161581930367" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/pipeline/zipball/7b52598c2e9105ebcddf247fc523161581930367", + "reference": "7b52598c2e9105ebcddf247fc523161581930367", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "php": ">=8.1", + "revolt/event-loop": "^1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.18" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Pipeline\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Asynchronous iterators and operators.", + "homepage": "https://amphp.org/pipeline", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "iterator", + "non-blocking" + ], + "support": { + "issues": "https://github.com/amphp/pipeline/issues", + "source": "https://github.com/amphp/pipeline/tree/v1.2.3" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2025-03-16T16:33:53+00:00" + }, + { + "name": "amphp/process", + "version": "v2.0.3", + "source": { + "type": "git", + "url": "https://github.com/amphp/process.git", + "reference": "52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/process/zipball/52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d", + "reference": "52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/sync": "^2", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Process\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A fiber-aware process manager based on Amp and Revolt.", + "homepage": "https://amphp.org/process", + "support": { + "issues": "https://github.com/amphp/process/issues", + "source": "https://github.com/amphp/process/tree/v2.0.3" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-04-19T03:13:44+00:00" + }, + { + "name": "amphp/serialization", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/serialization.git", + "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/serialization/zipball/693e77b2fb0b266c3c7d622317f881de44ae94a1", + "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "phpunit/phpunit": "^9 || ^8 || ^7" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Serialization\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Serialization tools for IPC and data storage in PHP.", + "homepage": "https://github.com/amphp/serialization", + "keywords": [ + "async", + "asynchronous", + "serialization", + "serialize" + ], + "support": { + "issues": "https://github.com/amphp/serialization/issues", + "source": "https://github.com/amphp/serialization/tree/master" + }, + "time": "2020-03-25T21:39:07+00:00" + }, + { + "name": "amphp/socket", + "version": "v2.3.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/socket.git", + "reference": "58e0422221825b79681b72c50c47a930be7bf1e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/socket/zipball/58e0422221825b79681b72c50c47a930be7bf1e1", + "reference": "58e0422221825b79681b72c50c47a930be7bf1e1", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/dns": "^2", + "ext-openssl": "*", + "kelunik/certificate": "^1.1", + "league/uri": "^6.5 | ^7", + "league/uri-interfaces": "^2.3 | ^7", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "amphp/process": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "5.20" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/Internal/functions.php", + "src/SocketAddress/functions.php" + ], + "psr-4": { + "Amp\\Socket\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@gmail.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Non-blocking socket connection / server implementations based on Amp and Revolt.", + "homepage": "https://github.com/amphp/socket", + "keywords": [ + "amp", + "async", + "encryption", + "non-blocking", + "sockets", + "tcp", + "tls" + ], + "support": { + "issues": "https://github.com/amphp/socket/issues", + "source": "https://github.com/amphp/socket/tree/v2.3.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-04-21T14:33:03+00:00" + }, + { + "name": "amphp/sync", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/sync.git", + "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/sync/zipball/217097b785130d77cfcc58ff583cf26cd1770bf1", + "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1", + "shasum": "" + }, + "require": { + "amphp/amp": "^3", + "amphp/pipeline": "^1", + "amphp/serialization": "^1", + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "amphp/phpunit-util": "^3", + "phpunit/phpunit": "^9", + "psalm/phar": "5.23" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Sync\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Stephen Coakley", + "email": "me@stephencoakley.com" + } + ], + "description": "Non-blocking synchronization primitives for PHP based on Amp and Revolt.", + "homepage": "https://github.com/amphp/sync", + "keywords": [ + "async", + "asynchronous", + "mutex", + "semaphore", + "synchronization" + ], + "support": { + "issues": "https://github.com/amphp/sync/issues", + "source": "https://github.com/amphp/sync/tree/v2.3.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2024-08-03T19:31:26+00:00" + }, + { + "name": "danog/advanced-json-rpc", + "version": "v3.2.2", + "source": { + "type": "git", + "url": "https://github.com/danog/php-advanced-json-rpc.git", + "reference": "aadb1c4068a88c3d0530cfe324b067920661efcb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/danog/php-advanced-json-rpc/zipball/aadb1c4068a88c3d0530cfe324b067920661efcb", + "reference": "aadb1c4068a88c3d0530cfe324b067920661efcb", + "shasum": "" + }, + "require": { + "netresearch/jsonmapper": "^5", + "php": ">=8.1", + "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0" + }, + "replace": { + "felixfbecker/php-advanced-json-rpc": "^3" + }, + "require-dev": { + "phpunit/phpunit": "^9" + }, + "type": "library", + "autoload": { + "psr-4": { + "AdvancedJsonRpc\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Felix Becker", + "email": "felix.b@outlook.com" + }, + { + "name": "Daniil Gentili", + "email": "daniil@daniil.it" + } + ], + "description": "A more advanced JSONRPC implementation", + "support": { + "issues": "https://github.com/danog/php-advanced-json-rpc/issues", + "source": "https://github.com/danog/php-advanced-json-rpc/tree/v3.2.2" + }, + "time": "2025-02-14T10:55:15+00:00" + }, + { + "name": "daverandom/libdns", + "version": "v2.1.0", + "source": { + "type": "git", + "url": "https://github.com/DaveRandom/LibDNS.git", + "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/DaveRandom/LibDNS/zipball/b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a", + "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "Required for IDN support" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "LibDNS\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "DNS protocol implementation written in pure PHP", + "keywords": [ + "dns" + ], + "support": { + "issues": "https://github.com/DaveRandom/LibDNS/issues", + "source": "https://github.com/DaveRandom/LibDNS/tree/v2.1.0" + }, + "time": "2024-04-12T12:12:48+00:00" + }, + { + "name": "dnoegel/php-xdg-base-dir", + "version": "v0.1.1", + "source": { + "type": "git", + "url": "https://github.com/dnoegel/php-xdg-base-dir.git", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "XdgBaseDir\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "implementation of xdg base directory specification for php", + "support": { + "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues", + "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1" + }, + "time": "2019-12-04T15:06:13+00:00" + }, + { + "name": "doctrine/data-fixtures", + "version": "1.8.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/data-fixtures.git", + "reference": "6fb221da56dae2011b33d47508e3b8aeb1d91db5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/data-fixtures/zipball/6fb221da56dae2011b33d47508e3b8aeb1d91db5", + "reference": "6fb221da56dae2011b33d47508e3b8aeb1d91db5", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^0.5.3 || ^1.0", + "doctrine/persistence": "^2.0 || ^3.0", + "php": "^7.4 || ^8.0", + "symfony/polyfill-php80": "^1" + }, + "conflict": { + "doctrine/dbal": "<3.5 || >=5", + "doctrine/orm": "<2.14 || >=4", + "doctrine/phpcr-odm": "<1.3.0" + }, + "require-dev": { + "doctrine/annotations": "^1.12 || ^2", + "doctrine/coding-standard": "^13", + "doctrine/dbal": "^3.5 || ^4", + "doctrine/mongodb-odm": "^1.3.0 || ^2.0.0", + "doctrine/orm": "^2.14 || ^3", + "ext-sqlite3": "*", + "fig/log-test": "^1", + "phpstan/phpstan": "2.1.17", + "phpunit/phpunit": "^9.6.13 || 10.5.45", + "psr/log": "^1.1 || ^2 || ^3", + "symfony/cache": "^5.4 || ^6.3 || ^7", + "symfony/var-exporter": "^5.4 || ^6.3 || ^7" + }, + "suggest": { + "alcaeus/mongo-php-adapter": "For using MongoDB ODM 1.3 with PHP 7 (deprecated)", + "doctrine/mongodb-odm": "For loading MongoDB ODM fixtures", + "doctrine/orm": "For loading ORM fixtures", + "doctrine/phpcr-odm": "For loading PHPCR ODM fixtures" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\DataFixtures\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + } + ], + "description": "Data Fixtures for all Doctrine Object Managers", + "homepage": "https://www.doctrine-project.org", + "keywords": [ + "database" + ], + "support": { + "issues": "https://github.com/doctrine/data-fixtures/issues", + "source": "https://github.com/doctrine/data-fixtures/tree/1.8.2" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdata-fixtures", + "type": "tidelift" + } + ], + "time": "2025-06-10T07:00:05+00:00" + }, + { + "name": "ergebnis/composer-normalize", + "version": "2.48.2", + "source": { + "type": "git", + "url": "https://github.com/ergebnis/composer-normalize.git", + "reference": "86dc9731b8320f49e9be9ad6d8e4de9b8b0e9b8b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ergebnis/composer-normalize/zipball/86dc9731b8320f49e9be9ad6d8e4de9b8b0e9b8b", + "reference": "86dc9731b8320f49e9be9ad6d8e4de9b8b0e9b8b", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0.0", + "ergebnis/json": "^1.4.0", + "ergebnis/json-normalizer": "^4.9.0", + "ergebnis/json-printer": "^3.7.0", + "ext-json": "*", + "justinrainbow/json-schema": "^5.2.12 || ^6.0.0", + "localheinz/diff": "^1.3.0", + "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" + }, + "require-dev": { + "composer/composer": "^2.8.3", + "ergebnis/license": "^2.7.0", + "ergebnis/php-cs-fixer-config": "^6.53.0", + "ergebnis/phpstan-rules": "^2.11.0", + "ergebnis/phpunit-slow-test-detector": "^2.20.0", + "fakerphp/faker": "^1.24.1", + "infection/infection": "~0.26.6", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^2.1.17", + "phpstan/phpstan-deprecation-rules": "^2.0.3", + "phpstan/phpstan-phpunit": "^2.0.7", + "phpstan/phpstan-strict-rules": "^2.0.6", + "phpunit/phpunit": "^9.6.20", + "rector/rector": "^2.1.4", + "symfony/filesystem": "^5.4.41" + }, + "type": "composer-plugin", + "extra": { + "class": "Ergebnis\\Composer\\Normalize\\NormalizePlugin", + "branch-alias": { + "dev-main": "2.49-dev" + }, + "plugin-optional": true, + "composer-normalize": { + "indent-size": 2, + "indent-style": "space" + } + }, + "autoload": { + "psr-4": { + "Ergebnis\\Composer\\Normalize\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andreas Möller", + "email": "am@localheinz.com", + "homepage": "https://localheinz.com" + } + ], + "description": "Provides a composer plugin for normalizing composer.json.", + "homepage": "https://github.com/ergebnis/composer-normalize", + "keywords": [ + "composer", + "normalize", + "normalizer", + "plugin" + ], + "support": { + "issues": "https://github.com/ergebnis/composer-normalize/issues", "security": "https://github.com/ergebnis/composer-normalize/blob/main/.github/SECURITY.md", "source": "https://github.com/ergebnis/composer-normalize" }, - "time": "2025-04-15T11:09:27+00:00" + "time": "2025-09-06T11:42:34+00:00" }, { "name": "ergebnis/json", - "version": "1.4.0", + "version": "1.6.0", "source": { "type": "git", "url": "https://github.com/ergebnis/json.git", - "reference": "7656ac2aa6c2ca4408f96f599e9a17a22c464f69" + "reference": "7b56d2b5d9e897e75b43e2e753075a0904c921b1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ergebnis/json/zipball/7656ac2aa6c2ca4408f96f599e9a17a22c464f69", - "reference": "7656ac2aa6c2ca4408f96f599e9a17a22c464f69", + "url": "https://api.github.com/repos/ergebnis/json/zipball/7b56d2b5d9e897e75b43e2e753075a0904c921b1", + "reference": "7b56d2b5d9e897e75b43e2e753075a0904c921b1", "shasum": "" }, "require": { "ext-json": "*", - "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" }, "require-dev": { + "ergebnis/composer-normalize": "^2.44.0", "ergebnis/data-provider": "^3.3.0", "ergebnis/license": "^2.5.0", "ergebnis/php-cs-fixer-config": "^6.37.0", + "ergebnis/phpstan-rules": "^2.11.0", "ergebnis/phpunit-slow-test-detector": "^2.16.1", "fakerphp/faker": "^1.24.0", "infection/infection": "~0.26.6", "phpstan/extension-installer": "^1.4.3", - "phpstan/phpstan": "^1.12.10", - "phpstan/phpstan-deprecation-rules": "^1.2.1", - "phpstan/phpstan-phpunit": "^1.4.0", - "phpstan/phpstan-strict-rules": "^1.6.1", - "phpunit/phpunit": "^9.6.18", - "rector/rector": "^1.2.10" + "phpstan/phpstan": "^2.1.22", + "phpstan/phpstan-deprecation-rules": "^2.0.3", + "phpstan/phpstan-phpunit": "^2.0.7", + "phpstan/phpstan-strict-rules": "^2.0.6", + "phpunit/phpunit": "^9.6.24", + "rector/rector": "^2.1.4" }, "type": "library", "extra": { + "branch-alias": { + "dev-main": "1.7-dev" + }, "composer-normalize": { "indent-size": 2, "indent-style": "space" @@ -11327,20 +12292,20 @@ "security": "https://github.com/ergebnis/json/blob/main/.github/SECURITY.md", "source": "https://github.com/ergebnis/json" }, - "time": "2024-11-17T11:51:22+00:00" + "time": "2025-09-06T09:08:45+00:00" }, { "name": "ergebnis/json-normalizer", - "version": "4.9.0", + "version": "4.10.1", "source": { "type": "git", "url": "https://github.com/ergebnis/json-normalizer.git", - "reference": "cc4dcf3890448572a2d9bea97133c4d860e59fb1" + "reference": "77961faf2c651c3f05977b53c6c68e8434febf62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ergebnis/json-normalizer/zipball/cc4dcf3890448572a2d9bea97133c4d860e59fb1", - "reference": "cc4dcf3890448572a2d9bea97133c4d860e59fb1", + "url": "https://api.github.com/repos/ergebnis/json-normalizer/zipball/77961faf2c651c3f05977b53c6c68e8434febf62", + "reference": "77961faf2c651c3f05977b53c6c68e8434febf62", "shasum": "" }, "require": { @@ -11350,7 +12315,7 @@ "ergebnis/json-schema-validator": "^4.2.0", "ext-json": "*", "justinrainbow/json-schema": "^5.2.12 || ^6.0.0", - "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" }, "require-dev": { "composer/semver": "^3.4.3", @@ -11375,7 +12340,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "4.8-dev" + "dev-main": "4.11-dev" }, "composer-normalize": { "indent-size": 2, @@ -11409,24 +12374,24 @@ "security": "https://github.com/ergebnis/json-normalizer/blob/main/.github/SECURITY.md", "source": "https://github.com/ergebnis/json-normalizer" }, - "time": "2025-04-10T13:13:04+00:00" + "time": "2025-09-06T09:18:13+00:00" }, { "name": "ergebnis/json-pointer", - "version": "3.6.0", + "version": "3.7.1", "source": { "type": "git", "url": "https://github.com/ergebnis/json-pointer.git", - "reference": "4fc85d8edb74466d282119d8d9541ec7cffc0798" + "reference": "43bef355184e9542635e35dd2705910a3df4c236" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ergebnis/json-pointer/zipball/4fc85d8edb74466d282119d8d9541ec7cffc0798", - "reference": "4fc85d8edb74466d282119d8d9541ec7cffc0798", + "url": "https://api.github.com/repos/ergebnis/json-pointer/zipball/43bef355184e9542635e35dd2705910a3df4c236", + "reference": "43bef355184e9542635e35dd2705910a3df4c236", "shasum": "" }, "require": { - "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" }, "require-dev": { "ergebnis/composer-normalize": "^2.43.0", @@ -11447,7 +12412,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.8-dev" }, "composer-normalize": { "indent-size": 2, @@ -11482,28 +12447,29 @@ "security": "https://github.com/ergebnis/json-pointer/blob/main/.github/SECURITY.md", "source": "https://github.com/ergebnis/json-pointer" }, - "time": "2024-11-17T12:37:06+00:00" + "time": "2025-09-06T09:28:19+00:00" }, { "name": "ergebnis/json-printer", - "version": "3.7.0", + "version": "3.8.1", "source": { "type": "git", "url": "https://github.com/ergebnis/json-printer.git", - "reference": "ced41fce7854152f0e8f38793c2ffe59513cdd82" + "reference": "211d73fc7ec6daf98568ee6ed6e6d133dee8503e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ergebnis/json-printer/zipball/ced41fce7854152f0e8f38793c2ffe59513cdd82", - "reference": "ced41fce7854152f0e8f38793c2ffe59513cdd82", + "url": "https://api.github.com/repos/ergebnis/json-printer/zipball/211d73fc7ec6daf98568ee6ed6e6d133dee8503e", + "reference": "211d73fc7ec6daf98568ee6ed6e6d133dee8503e", "shasum": "" }, "require": { "ext-json": "*", "ext-mbstring": "*", - "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" }, "require-dev": { + "ergebnis/composer-normalize": "^2.44.0", "ergebnis/data-provider": "^3.3.0", "ergebnis/license": "^2.5.0", "ergebnis/php-cs-fixer-config": "^6.37.0", @@ -11519,6 +12485,15 @@ "rector/rector": "^1.2.10" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.9-dev" + }, + "composer-normalize": { + "indent-size": 2, + "indent-style": "space" + } + }, "autoload": { "psr-4": { "Ergebnis\\Json\\Printer\\": "src/" @@ -11547,20 +12522,20 @@ "security": "https://github.com/ergebnis/json-printer/blob/main/.github/SECURITY.md", "source": "https://github.com/ergebnis/json-printer" }, - "time": "2024-11-17T11:20:51+00:00" + "time": "2025-09-06T09:59:26+00:00" }, { "name": "ergebnis/json-schema-validator", - "version": "4.4.0", + "version": "4.5.1", "source": { "type": "git", "url": "https://github.com/ergebnis/json-schema-validator.git", - "reference": "85f90c81f718aebba1d738800af83eeb447dc7ec" + "reference": "b739527a480a9e3651360ad351ea77e7e9019df2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ergebnis/json-schema-validator/zipball/85f90c81f718aebba1d738800af83eeb447dc7ec", - "reference": "85f90c81f718aebba1d738800af83eeb447dc7ec", + "url": "https://api.github.com/repos/ergebnis/json-schema-validator/zipball/b739527a480a9e3651360ad351ea77e7e9019df2", + "reference": "b739527a480a9e3651360ad351ea77e7e9019df2", "shasum": "" }, "require": { @@ -11568,7 +12543,7 @@ "ergebnis/json-pointer": "^3.4.0", "ext-json": "*", "justinrainbow/json-schema": "^5.2.12 || ^6.0.0", - "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" }, "require-dev": { "ergebnis/composer-normalize": "^2.44.0", @@ -11589,7 +12564,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "4.4-dev" + "dev-main": "4.6-dev" }, "composer-normalize": { "indent-size": 2, @@ -11624,7 +12599,7 @@ "security": "https://github.com/ergebnis/json-schema-validator/blob/main/.github/SECURITY.md", "source": "https://github.com/ergebnis/json-schema-validator" }, - "time": "2024-11-18T06:32:28+00:00" + "time": "2025-09-06T11:37:35+00:00" }, { "name": "fakerphp/faker", @@ -11690,149 +12665,327 @@ "time": "2024-11-21T13:46:39+00:00" }, { - "name": "felixfbecker/advanced-json-rpc", - "version": "v3.2.1", + "name": "felixfbecker/language-server-protocol", + "version": "v1.5.3", "source": { "type": "git", - "url": "https://github.com/felixfbecker/php-advanced-json-rpc.git", - "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447" + "url": "https://github.com/felixfbecker/php-language-server-protocol.git", + "reference": "a9e113dbc7d849e35b8776da39edaf4313b7b6c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/felixfbecker/php-advanced-json-rpc/zipball/b5f37dbff9a8ad360ca341f3240dc1c168b45447", - "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447", + "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/a9e113dbc7d849e35b8776da39edaf4313b7b6c9", + "reference": "a9e113dbc7d849e35b8776da39edaf4313b7b6c9", "shasum": "" }, "require": { - "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", - "php": "^7.1 || ^8.0", - "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0" + "php": ">=7.1" + }, + "require-dev": { + "phpstan/phpstan": "*", + "squizlabs/php_codesniffer": "^3.1", + "vimeo/psalm": "^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "LanguageServerProtocol\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Felix Becker", + "email": "felix.b@outlook.com" + } + ], + "description": "PHP classes for the Language Server Protocol", + "keywords": [ + "language", + "microsoft", + "php", + "server" + ], + "support": { + "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", + "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.3" + }, + "time": "2024-04-30T00:40:11+00:00" + }, + { + "name": "hautelook/alice-bundle", + "version": "2.13.0", + "source": { + "type": "git", + "url": "https://github.com/theofidry/AliceBundle.git", + "reference": "7b8cf62973853ec406ecb27f3b90b91a1b525a05" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/AliceBundle/zipball/7b8cf62973853ec406ecb27f3b90b91a1b525a05", + "reference": "7b8cf62973853ec406ecb27f3b90b91a1b525a05", + "shasum": "" + }, + "require": { + "doctrine/data-fixtures": "^1.5", + "doctrine/doctrine-bundle": "^2.5", + "doctrine/orm": "^2.10.0", + "doctrine/persistence": "^2.2 || ^3.0", + "php": "^8.2", + "psr/log": "^1.0 || ^2.0 || ^3.0", + "symfony/finder": "^6.4 || ^7.0", + "symfony/framework-bundle": "^6.4 || ^7.0", + "theofidry/alice-data-fixtures": "^1.5" + }, + "require-dev": { + "monolog/monolog": "^3.5", + "phpspec/prophecy": "^1.7", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "symfony/phpunit-bridge": "^6.4 || ^7.0" + }, + "type": "symfony-bundle", + "extra": { + "bin-dir": "bin", + "branch-alias": { + "dev-master": "2.x-dev" + }, + "sort-packages": true + }, + "autoload": { + "psr-4": { + "Hautelook\\AliceBundle\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Baldur Rensch", + "email": "brensch@gmail.com" + }, + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com", + "homepage": "https://github.com/theofidry" + } + ], + "description": "Symfony bundle to manage fixtures with Alice and Faker.", + "keywords": [ + "Fixture", + "alice", + "faker", + "orm", + "symfony" + ], + "support": { + "issues": "https://github.com/theofidry/AliceBundle/issues", + "source": "https://github.com/theofidry/AliceBundle/tree/2.13.0" + }, + "time": "2023-12-03T23:53:29+00:00" + }, + { + "name": "kelunik/certificate", + "version": "v1.1.3", + "source": { + "type": "git", + "url": "https://github.com/kelunik/certificate.git", + "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kelunik/certificate/zipball/7e00d498c264d5eb4f78c69f41c8bd6719c0199e", + "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "php": ">=7.0" }, "require-dev": { - "phpunit/phpunit": "^7.0 || ^8.0" + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^6 | 7 | ^8 | ^9" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, "autoload": { "psr-4": { - "AdvancedJsonRpc\\": "lib/" + "Kelunik\\Certificate\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "ISC" + "MIT" ], "authors": [ { - "name": "Felix Becker", - "email": "felix.b@outlook.com" + "name": "Niklas Keller", + "email": "me@kelunik.com" } ], - "description": "A more advanced JSONRPC implementation", + "description": "Access certificate details and transform between different formats.", + "keywords": [ + "DER", + "certificate", + "certificates", + "openssl", + "pem", + "x509" + ], "support": { - "issues": "https://github.com/felixfbecker/php-advanced-json-rpc/issues", - "source": "https://github.com/felixfbecker/php-advanced-json-rpc/tree/v3.2.1" + "issues": "https://github.com/kelunik/certificate/issues", + "source": "https://github.com/kelunik/certificate/tree/v1.1.3" }, - "time": "2021-06-11T22:34:44+00:00" + "time": "2023-02-03T21:26:53+00:00" }, { - "name": "felixfbecker/language-server-protocol", - "version": "v1.5.3", + "name": "league/uri", + "version": "7.6.0", "source": { "type": "git", - "url": "https://github.com/felixfbecker/php-language-server-protocol.git", - "reference": "a9e113dbc7d849e35b8776da39edaf4313b7b6c9" + "url": "https://github.com/thephpleague/uri.git", + "reference": "f625804987a0a9112d954f9209d91fec52182344" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/a9e113dbc7d849e35b8776da39edaf4313b7b6c9", - "reference": "a9e113dbc7d849e35b8776da39edaf4313b7b6c9", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/f625804987a0a9112d954f9209d91fec52182344", + "reference": "f625804987a0a9112d954f9209d91fec52182344", "shasum": "" }, "require": { - "php": ">=7.1" + "league/uri-interfaces": "^7.6", + "php": "^8.1", + "psr/http-factory": "^1" }, - "require-dev": { - "phpstan/phpstan": "*", - "squizlabs/php_codesniffer": "^3.1", - "vimeo/psalm": "^4.0" + "conflict": { + "league/uri-schemes": "^1.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-dom": "to convert the URI into an HTML anchor tag", + "ext-fileinfo": "to create Data URI from file contennts", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "ext-uri": "to use the PHP native URI class", + "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", + "league/uri-components": "Needed to easily manipulate URI objects components", + "league/uri-polyfill": "Needed to backport the PHP URI extension for older versions of PHP", + "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle WHATWG URL", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-master": "7.x-dev" } }, "autoload": { "psr-4": { - "LanguageServerProtocol\\": "src/" + "League\\Uri\\": "" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "ISC" + "MIT" ], "authors": [ { - "name": "Felix Becker", - "email": "felix.b@outlook.com" + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" } ], - "description": "PHP classes for the Language Server Protocol", + "description": "URI manipulation library", + "homepage": "https://uri.thephpleague.com", "keywords": [ - "language", - "microsoft", - "php", - "server" + "URN", + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "middleware", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc2141", + "rfc3986", + "rfc3987", + "rfc6570", + "rfc8141", + "uri", + "uri-template", + "url", + "ws" ], "support": { - "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", - "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.3" + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri/tree/7.6.0" }, - "time": "2024-04-30T00:40:11+00:00" + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2025-11-18T12:17:23+00:00" }, { - "name": "hautelook/alice-bundle", - "version": "2.13.0", + "name": "league/uri-interfaces", + "version": "7.6.0", "source": { "type": "git", - "url": "https://github.com/theofidry/AliceBundle.git", - "reference": "7b8cf62973853ec406ecb27f3b90b91a1b525a05" + "url": "https://github.com/thephpleague/uri-interfaces.git", + "reference": "ccbfb51c0445298e7e0b7f4481b942f589665368" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theofidry/AliceBundle/zipball/7b8cf62973853ec406ecb27f3b90b91a1b525a05", - "reference": "7b8cf62973853ec406ecb27f3b90b91a1b525a05", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/ccbfb51c0445298e7e0b7f4481b942f589665368", + "reference": "ccbfb51c0445298e7e0b7f4481b942f589665368", "shasum": "" }, "require": { - "doctrine/data-fixtures": "^1.5", - "doctrine/doctrine-bundle": "^2.5", - "doctrine/orm": "^2.10.0", - "doctrine/persistence": "^2.2 || ^3.0", - "php": "^8.2", - "psr/log": "^1.0 || ^2.0 || ^3.0", - "symfony/finder": "^6.4 || ^7.0", - "symfony/framework-bundle": "^6.4 || ^7.0", - "theofidry/alice-data-fixtures": "^1.5" + "ext-filter": "*", + "php": "^8.1", + "psr/http-message": "^1.1 || ^2.0" }, - "require-dev": { - "monolog/monolog": "^3.5", - "phpspec/prophecy": "^1.7", - "phpspec/prophecy-phpunit": "^2.0", - "phpunit/phpunit": "^9.5", - "symfony/phpunit-bridge": "^6.4 || ^7.0" + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle WHATWG URL", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, - "type": "symfony-bundle", + "type": "library", "extra": { - "bin-dir": "bin", "branch-alias": { - "dev-master": "2.x-dev" - }, - "sort-packages": true + "dev-master": "7.x-dev" + } }, "autoload": { "psr-4": { - "Hautelook\\AliceBundle\\": "src" + "League\\Uri\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -11841,45 +12994,62 @@ ], "authors": [ { - "name": "Baldur Rensch", - "email": "brensch@gmail.com" - }, - { - "name": "Théo FIDRY", - "email": "theo.fidry@gmail.com", - "homepage": "https://github.com/theofidry" + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" } ], - "description": "Symfony bundle to manage fixtures with Alice and Faker.", + "description": "Common tools for parsing and resolving RFC3987/RFC3986 URI", + "homepage": "https://uri.thephpleague.com", "keywords": [ - "Fixture", - "alice", - "faker", - "orm", - "symfony" + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "url", + "ws" ], "support": { - "issues": "https://github.com/theofidry/AliceBundle/issues", - "source": "https://github.com/theofidry/AliceBundle/tree/2.13.0" + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.6.0" }, - "time": "2023-12-03T23:53:29+00:00" + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2025-11-18T12:17:23+00:00" }, { "name": "localheinz/diff", - "version": "1.2.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/localheinz/diff.git", - "reference": "ec413943c2b518464865673fd5b38f7df867a010" + "reference": "33bd840935970cda6691c23fc7d94ae764c0734c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/localheinz/diff/zipball/ec413943c2b518464865673fd5b38f7df867a010", - "reference": "ec413943c2b518464865673fd5b38f7df867a010", + "url": "https://api.github.com/repos/localheinz/diff/zipball/33bd840935970cda6691c23fc7d94ae764c0734c", + "reference": "33bd840935970cda6691c23fc7d94ae764c0734c", "shasum": "" }, "require": { - "php": "~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + "php": "~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" }, "require-dev": { "phpunit/phpunit": "^7.5.0 || ^8.5.23", @@ -11915,22 +13085,22 @@ ], "support": { "issues": "https://github.com/localheinz/diff/issues", - "source": "https://github.com/localheinz/diff/tree/1.2.0" + "source": "https://github.com/localheinz/diff/tree/1.3.0" }, - "time": "2024-12-04T14:16:01+00:00" + "time": "2025-08-30T09:44:18+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.13.1", + "version": "1.13.4", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c" + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c", - "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", "shasum": "" }, "require": { @@ -11969,7 +13139,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" }, "funding": [ { @@ -11977,7 +13147,7 @@ "type": "tidelift" } ], - "time": "2025-04-29T12:36:36+00:00" + "time": "2025-08-01T08:46:24+00:00" }, { "name": "nelmio/alice", @@ -12077,16 +13247,16 @@ }, { "name": "netresearch/jsonmapper", - "version": "v4.5.0", + "version": "v5.0.0", "source": { "type": "git", "url": "https://github.com/cweiske/jsonmapper.git", - "reference": "8e76efb98ee8b6afc54687045e1b8dba55ac76e5" + "reference": "8c64d8d444a5d764c641ebe97e0e3bc72b25bf6c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8e76efb98ee8b6afc54687045e1b8dba55ac76e5", - "reference": "8e76efb98ee8b6afc54687045e1b8dba55ac76e5", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8c64d8d444a5d764c641ebe97e0e3bc72b25bf6c", + "reference": "8c64d8d444a5d764c641ebe97e0e3bc72b25bf6c", "shasum": "" }, "require": { @@ -12122,31 +13292,33 @@ "support": { "email": "cweiske@cweiske.de", "issues": "https://github.com/cweiske/jsonmapper/issues", - "source": "https://github.com/cweiske/jsonmapper/tree/v4.5.0" + "source": "https://github.com/cweiske/jsonmapper/tree/v5.0.0" }, - "time": "2024-09-08T10:13:13+00:00" + "time": "2024-09-08T10:20:00+00:00" }, { "name": "nikic/php-parser", - "version": "v4.19.4", + "version": "v5.6.2", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "715f4d25e225bc47b293a8b997fe6ce99bf987d2" + "reference": "3a454ca033b9e06b63282ce19562e892747449bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/715f4d25e225bc47b293a8b997fe6ce99bf987d2", - "reference": "715f4d25e225bc47b293a8b997fe6ce99bf987d2", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb", + "reference": "3a454ca033b9e06b63282ce19562e892747449bb", "shasum": "" }, "require": { + "ext-ctype": "*", + "ext-json": "*", "ext-tokenizer": "*", - "php": ">=7.1" + "php": ">=7.4" }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^9.0" }, "bin": [ "bin/php-parse" @@ -12154,7 +13326,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.9-dev" + "dev-master": "5.x-dev" } }, "autoload": { @@ -12178,9 +13350,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.19.4" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2" }, - "time": "2024-09-29T15:01:53+00:00" + "time": "2025-10-21T19:32:17+00:00" }, { "name": "phar-io/manifest", @@ -12302,16 +13474,11 @@ }, { "name": "phpstan/phpstan", - "version": "1.12.27", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpstan.git", - "reference": "3a6e423c076ab39dfedc307e2ac627ef579db162" - }, + "version": "1.12.32", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/3a6e423c076ab39dfedc307e2ac627ef579db162", - "reference": "3a6e423c076ab39dfedc307e2ac627ef579db162", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/2770dcdf5078d0b0d53f94317e06affe88419aa8", + "reference": "2770dcdf5078d0b0d53f94317e06affe88419aa8", "shasum": "" }, "require": { @@ -12356,7 +13523,7 @@ "type": "github" } ], - "time": "2025-05-21T20:51:45+00:00" + "time": "2025-09-30T10:16:31+00:00" }, { "name": "phpunit/php-code-coverage", @@ -12679,16 +13846,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.23", + "version": "9.6.29", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95" + "reference": "9ecfec57835a5581bc888ea7e13b51eb55ab9dd3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/43d2cb18d0675c38bd44982a5d1d88f6d53d8d95", - "reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9ecfec57835a5581bc888ea7e13b51eb55ab9dd3", + "reference": "9ecfec57835a5581bc888ea7e13b51eb55ab9dd3", "shasum": "" }, "require": { @@ -12699,7 +13866,7 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.13.1", + "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=7.3", @@ -12710,11 +13877,11 @@ "phpunit/php-timer": "^5.0.3", "sebastian/cli-parser": "^1.0.2", "sebastian/code-unit": "^1.0.8", - "sebastian/comparator": "^4.0.8", + "sebastian/comparator": "^4.0.9", "sebastian/diff": "^4.0.6", "sebastian/environment": "^5.1.5", - "sebastian/exporter": "^4.0.6", - "sebastian/global-state": "^5.0.7", + "sebastian/exporter": "^4.0.8", + "sebastian/global-state": "^5.0.8", "sebastian/object-enumerator": "^4.0.4", "sebastian/resource-operations": "^3.0.4", "sebastian/type": "^3.2.1", @@ -12762,7 +13929,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.23" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.29" }, "funding": [ { @@ -12786,27 +13953,27 @@ "type": "tidelift" } ], - "time": "2025-05-02T06:40:34+00:00" + "time": "2025-09-24T06:29:11+00:00" }, { "name": "psalm/plugin-symfony", - "version": "v5.2.6", + "version": "v5.2.7", "source": { "type": "git", "url": "https://github.com/psalm/psalm-plugin-symfony.git", - "reference": "1638f172b4ae5f283ee6b1b58fff3e44aa628742" + "reference": "56dd7ba14c4ac9796e06077f00e45fbbecc640af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/psalm/psalm-plugin-symfony/zipball/1638f172b4ae5f283ee6b1b58fff3e44aa628742", - "reference": "1638f172b4ae5f283ee6b1b58fff3e44aa628742", + "url": "https://api.github.com/repos/psalm/psalm-plugin-symfony/zipball/56dd7ba14c4ac9796e06077f00e45fbbecc640af", + "reference": "56dd7ba14c4ac9796e06077f00e45fbbecc640af", "shasum": "" }, "require": { "ext-simplexml": "*", - "php": "^8.1", + "php": ">=8.1", "symfony/framework-bundle": "^5.0 || ^6.0 || ^7.0", - "vimeo/psalm": "^5.16 || dev-master" + "vimeo/psalm": "^6 || dev-master" }, "require-dev": { "doctrine/annotations": "^1.8|^2", @@ -12849,9 +14016,9 @@ "description": "Psalm Plugin for Symfony", "support": { "issues": "https://github.com/psalm/psalm-plugin-symfony/issues", - "source": "https://github.com/psalm/psalm-plugin-symfony/tree/v5.2.6" + "source": "https://github.com/psalm/psalm-plugin-symfony/tree/v5.2.7" }, - "time": "2024-12-21T08:46:26+00:00" + "time": "2025-01-26T10:27:48+00:00" }, { "name": "rector/rector", @@ -12912,6 +14079,78 @@ ], "time": "2024-11-08T13:59:10+00:00" }, + { + "name": "revolt/event-loop", + "version": "v1.0.8", + "source": { + "type": "git", + "url": "https://github.com/revoltphp/event-loop.git", + "reference": "b6fc06dce8e9b523c9946138fa5e62181934f91c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/revoltphp/event-loop/zipball/b6fc06dce8e9b523c9946138fa5e62181934f91c", + "reference": "b6fc06dce8e9b523c9946138fa5e62181934f91c", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "ext-json": "*", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.15" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Revolt\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "ceesjank@gmail.com" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Rock-solid event loop for concurrent PHP applications.", + "keywords": [ + "async", + "asynchronous", + "concurrency", + "event", + "event-loop", + "non-blocking", + "scheduler" + ], + "support": { + "issues": "https://github.com/revoltphp/event-loop/issues", + "source": "https://github.com/revoltphp/event-loop/tree/v1.0.8" + }, + "time": "2025-08-27T21:33:23+00:00" + }, { "name": "sebastian/cli-parser", "version": "1.0.2", @@ -13081,16 +14320,16 @@ }, { "name": "sebastian/comparator", - "version": "4.0.8", + "version": "4.0.9", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + "reference": "67a2df3a62639eab2cc5906065e9805d4fd5dfc5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/67a2df3a62639eab2cc5906065e9805d4fd5dfc5", + "reference": "67a2df3a62639eab2cc5906065e9805d4fd5dfc5", "shasum": "" }, "require": { @@ -13143,15 +14382,27 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.9" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" } ], - "time": "2022-09-14T12:41:17+00:00" + "time": "2025-08-10T06:51:50+00:00" }, { "name": "sebastian/complexity", @@ -13275,16 +14526,16 @@ }, { "name": "sebastian/exporter", - "version": "4.0.6", + "version": "4.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" + "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", - "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/14c6ba52f95a36c3d27c835d65efc7123c446e8c", + "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c", "shasum": "" }, "require": { @@ -13340,28 +14591,40 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.8" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" } ], - "time": "2024-03-02T06:33:00+00:00" + "time": "2025-09-24T06:03:27+00:00" }, { "name": "sebastian/global-state", - "version": "5.0.7", + "version": "5.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" + "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", - "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/b6781316bdcd28260904e7cc18ec983d0d2ef4f6", + "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6", "shasum": "" }, "require": { @@ -13404,15 +14667,27 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.8" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", + "type": "tidelift" } ], - "time": "2024-03-02T06:35:11+00:00" + "time": "2025-08-10T07:10:35+00:00" }, { "name": "sebastian/lines-of-code", @@ -13585,16 +14860,16 @@ }, { "name": "sebastian/recursion-context", - "version": "4.0.5", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + "reference": "539c6691e0623af6dc6f9c20384c120f963465a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/539c6691e0623af6dc6f9c20384c120f963465a0", + "reference": "539c6691e0623af6dc6f9c20384c120f963465a0", "shasum": "" }, "require": { @@ -13636,15 +14911,27 @@ "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.6" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" } ], - "time": "2023-02-03T06:07:39+00:00" + "time": "2025-08-10T06:57:39+00:00" }, { "name": "sebastian/resource-operations", @@ -13811,16 +15098,16 @@ }, { "name": "spatie/array-to-xml", - "version": "3.4.0", + "version": "3.4.3", "source": { "type": "git", "url": "https://github.com/spatie/array-to-xml.git", - "reference": "7dcfc67d60b0272926dabad1ec01f6b8a5fb5e67" + "reference": "7b9202dccfe18d4e3a13303156d6bbcc1c61dabf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/7dcfc67d60b0272926dabad1ec01f6b8a5fb5e67", - "reference": "7dcfc67d60b0272926dabad1ec01f6b8a5fb5e67", + "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/7b9202dccfe18d4e3a13303156d6bbcc1c61dabf", + "reference": "7b9202dccfe18d4e3a13303156d6bbcc1c61dabf", "shasum": "" }, "require": { @@ -13863,7 +15150,7 @@ "xml" ], "support": { - "source": "https://github.com/spatie/array-to-xml/tree/3.4.0" + "source": "https://github.com/spatie/array-to-xml/tree/3.4.3" }, "funding": [ { @@ -13875,20 +15162,20 @@ "type": "github" } ], - "time": "2024-12-16T12:45:15+00:00" + "time": "2025-11-27T09:08:26+00:00" }, { "name": "symfony/browser-kit", - "version": "v6.4.19", + "version": "v6.4.28", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "ce95f3e3239159e7fa3be7690c6ce95a4714637f" + "reference": "067e301786bbb58048077fc10507aceb18226e23" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/ce95f3e3239159e7fa3be7690c6ce95a4714637f", - "reference": "ce95f3e3239159e7fa3be7690c6ce95a4714637f", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/067e301786bbb58048077fc10507aceb18226e23", + "reference": "067e301786bbb58048077fc10507aceb18226e23", "shasum": "" }, "require": { @@ -13927,7 +15214,7 @@ "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/browser-kit/tree/v6.4.19" + "source": "https://github.com/symfony/browser-kit/tree/v6.4.28" }, "funding": [ { @@ -13938,25 +15225,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-02-14T11:23:16+00:00" + "time": "2025-10-16T22:35:35+00:00" }, { "name": "symfony/css-selector", - "version": "v6.4.13", + "version": "v6.4.24", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "cb23e97813c5837a041b73a6d63a9ddff0778f5e" + "reference": "9b784413143701aa3c94ac1869a159a9e53e8761" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/cb23e97813c5837a041b73a6d63a9ddff0778f5e", - "reference": "cb23e97813c5837a041b73a6d63a9ddff0778f5e", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/9b784413143701aa3c94ac1869a159a9e53e8761", + "reference": "9b784413143701aa3c94ac1869a159a9e53e8761", "shasum": "" }, "require": { @@ -13992,7 +15283,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v6.4.13" + "source": "https://github.com/symfony/css-selector/tree/v6.4.24" }, "funding": [ { @@ -14003,25 +15294,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:18:03+00:00" + "time": "2025-07-10T08:14:14+00:00" }, { "name": "symfony/debug-bundle", - "version": "v6.4.13", + "version": "v6.4.27", "source": { "type": "git", "url": "https://github.com/symfony/debug-bundle.git", - "reference": "7bcfaff39e094cc09455201916d016d9b2ae08ff" + "reference": "21a61c55192d558a6b81cdb12e8c010fc9474fe0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug-bundle/zipball/7bcfaff39e094cc09455201916d016d9b2ae08ff", - "reference": "7bcfaff39e094cc09455201916d016d9b2ae08ff", + "url": "https://api.github.com/repos/symfony/debug-bundle/zipball/21a61c55192d558a6b81cdb12e8c010fc9474fe0", + "reference": "21a61c55192d558a6b81cdb12e8c010fc9474fe0", "shasum": "" }, "require": { @@ -14066,7 +15361,7 @@ "description": "Provides a tight integration of the Symfony VarDumper component and the ServerLogCommand from MonologBridge into the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/debug-bundle/tree/v6.4.13" + "source": "https://github.com/symfony/debug-bundle/tree/v6.4.27" }, "funding": [ { @@ -14077,40 +15372,44 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:18:03+00:00" + "time": "2025-10-11T17:35:31+00:00" }, { "name": "symfony/maker-bundle", - "version": "v1.62.1", + "version": "v1.65.0", "source": { "type": "git", "url": "https://github.com/symfony/maker-bundle.git", - "reference": "468ff2708200c95ebc0d85d3174b6c6711b8a590" + "reference": "9a0276d7486b29cae641b4a0a85d5e5cc149bff2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/maker-bundle/zipball/468ff2708200c95ebc0d85d3174b6c6711b8a590", - "reference": "468ff2708200c95ebc0d85d3174b6c6711b8a590", + "url": "https://api.github.com/repos/symfony/maker-bundle/zipball/9a0276d7486b29cae641b4a0a85d5e5cc149bff2", + "reference": "9a0276d7486b29cae641b4a0a85d5e5cc149bff2", "shasum": "" }, "require": { "doctrine/inflector": "^2.0", - "nikic/php-parser": "^4.18|^5.0", + "nikic/php-parser": "^5.0", "php": ">=8.1", - "symfony/config": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", "symfony/deprecation-contracts": "^2.2|^3", - "symfony/filesystem": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", - "symfony/framework-bundle": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0" + "symfony/filesystem": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/framework-bundle": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0" }, "conflict": { "doctrine/doctrine-bundle": "<2.10", @@ -14118,12 +15417,14 @@ }, "require-dev": { "composer/semver": "^3.0", - "doctrine/doctrine-bundle": "^2.5.0", + "doctrine/doctrine-bundle": "^2.5.0|^3.0.0", "doctrine/orm": "^2.15|^3", - "symfony/http-client": "^6.4|^7.0", - "symfony/phpunit-bridge": "^6.4.1|^7.0", - "symfony/security-core": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0", + "doctrine/persistence": "^3.1|^4.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/phpunit-bridge": "^6.4.1|^7.0|^8.0", + "symfony/security-core": "^6.4|^7.0|^8.0", + "symfony/security-http": "^6.4|^7.0|^8.0", + "symfony/yaml": "^6.4|^7.0|^8.0", "twig/twig": "^3.0|^4.x-dev" }, "type": "symfony-bundle", @@ -14158,7 +15459,7 @@ ], "support": { "issues": "https://github.com/symfony/maker-bundle/issues", - "source": "https://github.com/symfony/maker-bundle/tree/v1.62.1" + "source": "https://github.com/symfony/maker-bundle/tree/v1.65.0" }, "funding": [ { @@ -14169,25 +15470,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-15T00:21:40+00:00" + "time": "2025-11-24T15:41:51+00:00" }, { "name": "symfony/phpunit-bridge", - "version": "v6.4.23", + "version": "v6.4.26", "source": { "type": "git", "url": "https://github.com/symfony/phpunit-bridge.git", - "reference": "0d26168bf78993b3c49e69e41bea3e7cbecc426c" + "reference": "406aa80401bf960e7a173a3ccf268ae82b6bc93f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/0d26168bf78993b3c49e69e41bea3e7cbecc426c", - "reference": "0d26168bf78993b3c49e69e41bea3e7cbecc426c", + "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/406aa80401bf960e7a173a3ccf268ae82b6bc93f", + "reference": "406aa80401bf960e7a173a3ccf268ae82b6bc93f", "shasum": "" }, "require": { @@ -14243,7 +15548,7 @@ "testing" ], "support": { - "source": "https://github.com/symfony/phpunit-bridge/tree/v6.4.23" + "source": "https://github.com/symfony/phpunit-bridge/tree/v6.4.26" }, "funding": [ { @@ -14254,25 +15559,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-06-04T07:29:26+00:00" + "time": "2025-09-12T08:37:02+00:00" }, { "name": "symfony/web-profiler-bundle", - "version": "v6.4.19", + "version": "v6.4.27", "source": { "type": "git", "url": "https://github.com/symfony/web-profiler-bundle.git", - "reference": "7d1026a8e950d416cb5148ae88ac23db5d264839" + "reference": "4c2ab411372e8bd854678cd7c81f1a9bfd6914aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/7d1026a8e950d416cb5148ae88ac23db5d264839", - "reference": "7d1026a8e950d416cb5148ae88ac23db5d264839", + "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/4c2ab411372e8bd854678cd7c81f1a9bfd6914aa", + "reference": "4c2ab411372e8bd854678cd7c81f1a9bfd6914aa", "shasum": "" }, "require": { @@ -14325,7 +15634,7 @@ "dev" ], "support": { - "source": "https://github.com/symfony/web-profiler-bundle/tree/v6.4.19" + "source": "https://github.com/symfony/web-profiler-bundle/tree/v6.4.27" }, "funding": [ { @@ -14336,12 +15645,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-02-14T12:21:59+00:00" + "time": "2025-10-05T13:55:43+00:00" }, { "name": "theofidry/alice-data-fixtures", @@ -14446,16 +15759,16 @@ }, { "name": "theseer/tokenizer", - "version": "1.2.3", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", "shasum": "" }, "require": { @@ -14484,7 +15797,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" }, "funding": [ { @@ -14492,28 +15805,30 @@ "type": "github" } ], - "time": "2024-03-03T12:36:25+00:00" + "time": "2025-11-17T20:03:58+00:00" }, { "name": "vimeo/psalm", - "version": "5.26.1", + "version": "6.13.1", "source": { "type": "git", "url": "https://github.com/vimeo/psalm.git", - "reference": "d747f6500b38ac4f7dfc5edbcae6e4b637d7add0" + "reference": "1e3b7f0a8ab32b23197b91107adc0a7ed8a05b51" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/d747f6500b38ac4f7dfc5edbcae6e4b637d7add0", - "reference": "d747f6500b38ac4f7dfc5edbcae6e4b637d7add0", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/1e3b7f0a8ab32b23197b91107adc0a7ed8a05b51", + "reference": "1e3b7f0a8ab32b23197b91107adc0a7ed8a05b51", "shasum": "" }, "require": { - "amphp/amp": "^2.4.2", - "amphp/byte-stream": "^1.5", + "amphp/amp": "^3", + "amphp/byte-stream": "^2", + "amphp/parallel": "^2.3", "composer-runtime-api": "^2", "composer/semver": "^1.4 || ^2.0 || ^3.0", "composer/xdebug-handler": "^2.0 || ^3.0", + "danog/advanced-json-rpc": "^3.1", "dnoegel/php-xdg-base-dir": "^0.1.1", "ext-ctype": "*", "ext-dom": "*", @@ -14522,27 +15837,26 @@ "ext-mbstring": "*", "ext-simplexml": "*", "ext-tokenizer": "*", - "felixfbecker/advanced-json-rpc": "^3.1", - "felixfbecker/language-server-protocol": "^1.5.2", + "felixfbecker/language-server-protocol": "^1.5.3", "fidry/cpu-core-counter": "^0.4.1 || ^0.5.1 || ^1.0.0", - "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", - "nikic/php-parser": "^4.17", - "php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0", - "sebastian/diff": "^4.0 || ^5.0 || ^6.0", + "netresearch/jsonmapper": "^5.0", + "nikic/php-parser": "^5.0.0", + "php": "~8.1.31 || ~8.2.27 || ~8.3.16 || ~8.4.3", + "sebastian/diff": "^4.0 || ^5.0 || ^6.0 || ^7.0", "spatie/array-to-xml": "^2.17.0 || ^3.0", - "symfony/console": "^4.1.6 || ^5.0 || ^6.0 || ^7.0", - "symfony/filesystem": "^5.4 || ^6.0 || ^7.0" - }, - "conflict": { - "nikic/php-parser": "4.17.0" + "symfony/console": "^6.0 || ^7.0", + "symfony/filesystem": "~6.3.12 || ~6.4.3 || ^7.0.3", + "symfony/polyfill-php84": "^1.31.0" }, "provide": { "psalm/psalm": "self.version" }, "require-dev": { - "amphp/phpunit-util": "^2.0", + "amphp/phpunit-util": "^3", "bamarni/composer-bin-plugin": "^1.4", "brianium/paratest": "^6.9", + "danog/class-finder": "^0.4.8", + "dg/bypass-finals": "^1.5", "ext-curl": "*", "mockery/mockery": "^1.5", "nunomaduro/mock-final-classes": "^1.1", @@ -14550,10 +15864,10 @@ "phpstan/phpdoc-parser": "^1.6", "phpunit/phpunit": "^9.6", "psalm/plugin-mockery": "^1.1", - "psalm/plugin-phpunit": "^0.18", + "psalm/plugin-phpunit": "^0.19", "slevomat/coding-standard": "^8.4", "squizlabs/php_codesniffer": "^3.6", - "symfony/process": "^4.4 || ^5.0 || ^6.0 || ^7.0" + "symfony/process": "^6.0 || ^7.0" }, "suggest": { "ext-curl": "In order to send data to shepherd", @@ -14564,6 +15878,7 @@ "psalm-language-server", "psalm-plugin", "psalm-refactor", + "psalm-review", "psalter" ], "type": "project", @@ -14573,7 +15888,9 @@ "dev-2.x": "2.x-dev", "dev-3.x": "3.x-dev", "dev-4.x": "4.x-dev", - "dev-master": "5.x-dev" + "dev-5.x": "5.x-dev", + "dev-6.x": "6.x-dev", + "dev-master": "7.x-dev" } }, "autoload": { @@ -14588,6 +15905,10 @@ "authors": [ { "name": "Matthew Brown" + }, + { + "name": "Daniil Gentili", + "email": "daniil@daniil.it" } ], "description": "A static analysis tool for finding errors in PHP applications", @@ -14602,20 +15923,20 @@ "issues": "https://github.com/vimeo/psalm/issues", "source": "https://github.com/vimeo/psalm" }, - "time": "2024-09-08T18:53:08+00:00" + "time": "2025-08-06T10:10:28+00:00" }, { "name": "vincentlanglet/twig-cs-fixer", - "version": "3.8.1", + "version": "3.11.0", "source": { "type": "git", "url": "https://github.com/VincentLanglet/Twig-CS-Fixer.git", - "reference": "4e330681d38579a3354bb3dff4b357118baab32c" + "reference": "866af065fd09980b6390ee5c69e45b08053101e8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/VincentLanglet/Twig-CS-Fixer/zipball/4e330681d38579a3354bb3dff4b357118baab32c", - "reference": "4e330681d38579a3354bb3dff4b357118baab32c", + "url": "https://api.github.com/repos/VincentLanglet/Twig-CS-Fixer/zipball/866af065fd09980b6390ee5c69e45b08053101e8", + "reference": "866af065fd09980b6390ee5c69e45b08053101e8", "shasum": "" }, "require": { @@ -14623,10 +15944,10 @@ "ext-ctype": "*", "ext-json": "*", "php": ">=8.0", - "symfony/console": "^5.4.9 || ^6.4 || ^7.0", - "symfony/filesystem": "^5.4 || ^6.4 || ^7.0", - "symfony/finder": "^5.4 || ^6.4 || ^7.0", - "symfony/string": "^5.4.42 || ^6.4.10 || ~7.0.10 || ^7.1.3", + "symfony/console": "^5.4.9 || ^6.4 || ^7.0 || ^8.0", + "symfony/filesystem": "^5.4 || ^6.4 || ^7.0 || ^8.0", + "symfony/finder": "^5.4 || ^6.4 || ^7.0 || ^8.0", + "symfony/string": "^5.4.42 || ^6.4.10 || ~7.0.10 || ^7.1.3 || ^8.0", "twig/twig": "^3.4", "webmozart/assert": "^1.10" }, @@ -14644,8 +15965,8 @@ "phpunit/phpunit": "^9.5.26 || ^11.5.18 || ^12.1.3", "rector/rector": "^2.0.0", "shipmonk/composer-dependency-analyser": "^1.6", - "symfony/process": "^5.4 || ^6.4 || ^7.0", - "symfony/twig-bridge": "^5.4 || ^6.4 || ^7.0", + "symfony/process": "^5.4 || ^6.4 || ^7.0 || ^8.0", + "symfony/twig-bridge": "^5.4 || ^6.4 || ^7.0 || ^8.0", "symfony/ux-twig-component": "^2.2.0", "twig/cache-extra": "^3.2" }, @@ -14671,7 +15992,7 @@ "homepage": "https://github.com/VincentLanglet/Twig-CS-Fixer", "support": { "issues": "https://github.com/VincentLanglet/Twig-CS-Fixer/issues", - "source": "https://github.com/VincentLanglet/Twig-CS-Fixer/tree/3.8.1" + "source": "https://github.com/VincentLanglet/Twig-CS-Fixer/tree/3.11.0" }, "funding": [ { @@ -14679,26 +16000,26 @@ "type": "github" } ], - "time": "2025-06-18T07:46:36+00:00" + "time": "2025-11-24T18:13:18+00:00" }, { "name": "weirdan/doctrine-psalm-plugin", - "version": "v2.9.0", + "version": "v2.10.0", "source": { "type": "git", "url": "https://github.com/psalm/psalm-plugin-doctrine.git", - "reference": "3db8e55b2ea15373338d2a3eab71c5f5a31c8b08" + "reference": "8d604c817976e156cd6f1cfab983eadd35b04a2f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/psalm/psalm-plugin-doctrine/zipball/3db8e55b2ea15373338d2a3eab71c5f5a31c8b08", - "reference": "3db8e55b2ea15373338d2a3eab71c5f5a31c8b08", + "url": "https://api.github.com/repos/psalm/psalm-plugin-doctrine/zipball/8d604c817976e156cd6f1cfab983eadd35b04a2f", + "reference": "8d604c817976e156cd6f1cfab983eadd35b04a2f", "shasum": "" }, "require": { "composer/semver": "^1.4 || ^2.0 || ^3.0", - "php": "^7.2 || ^8", - "vimeo/psalm": "^4.28|^5.0" + "php": "^8", + "vimeo/psalm": "^6" }, "conflict": { "doctrine/collections": "<1.8", @@ -14755,9 +16076,9 @@ ], "support": { "issues": "https://github.com/psalm/psalm-plugin-doctrine/issues", - "source": "https://github.com/psalm/psalm-plugin-doctrine/tree/v2.9.0" + "source": "https://github.com/psalm/psalm-plugin-doctrine/tree/v2.10.0" }, - "time": "2023-07-15T05:44:30+00:00" + "time": "2025-01-26T11:36:27+00:00" } ], "aliases": [], @@ -14773,5 +16094,5 @@ "ext-iconv": "*" }, "platform-dev": {}, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.9.0" } diff --git a/package-lock.json b/package-lock.json index d959cccb0..f27fef983 100644 --- a/package-lock.json +++ b/package-lock.json @@ -187,6 +187,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", @@ -1229,6 +1230,7 @@ "integrity": "sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA==", "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "@fortawesome/fontawesome-common-types": "^0.2.36" }, @@ -1768,6 +1770,7 @@ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", "license": "MIT", + "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" @@ -1803,7 +1806,8 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@reduxjs/toolkit/node_modules/redux-thunk": { "version": "3.1.0", @@ -2518,6 +2522,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.23.tgz", "integrity": "sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==", "license": "MIT", + "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -2817,6 +2822,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001726", "electron-to-chromium": "^1.5.173", @@ -3215,6 +3221,7 @@ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "license": "ISC", + "peer": true, "engines": { "node": ">=12" } @@ -3561,6 +3568,7 @@ "devOptional": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -4091,6 +4099,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.17.2" } @@ -5461,6 +5470,7 @@ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -5611,6 +5621,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -5664,6 +5675,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -5756,8 +5768,7 @@ "version": "19.1.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.1.tgz", "integrity": "sha512-tr41fA15Vn8p4X9ntI+yCyeGSf1TlYaY5vlTZfQmeLBrFo3psOPX6HhTDnFNL9uj3EhP0KAQ80cugCl4b4BERA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/react-lifecycles-compat": { "version": "3.0.4", @@ -5833,6 +5844,7 @@ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz", "integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.15.4", "@types/react-redux": "^7.1.20", @@ -6032,6 +6044,7 @@ "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.9.2" } @@ -6249,6 +6262,7 @@ "integrity": "sha512-9GUyuksjw70uNpb1MTYWsH9MQHOHY6kwfnkafC24+7aOMZn9+rVMBxRbLvw756mrBFbIsFg6Xw9IkR2Fnn3k+Q==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.0.2", @@ -6816,6 +6830,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -6871,6 +6886,7 @@ "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6997,6 +7013,7 @@ "resolved": "https://registry.npmjs.org/rolldown-vite/-/rolldown-vite-7.0.12.tgz", "integrity": "sha512-Gr40FRnE98FwPJcMwcJgBwP6U7Qxw/VEtDsFdFjvGUTdgI/tTmF7z7dbVo/ajItM54G+Zo9w5BIrUmat6MbuWQ==", "license": "MIT", + "peer": true, "dependencies": { "fdir": "^6.4.6", "lightningcss": "^1.30.1", @@ -7130,6 +7147,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, From f283e945a26dba59846d9520062ef2002e507139 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Mon, 8 Dec 2025 08:49:40 +0100 Subject: [PATCH 07/59] 5402: Refactor image build to better use configuration options from underlying images --- .dockerignore | 13 +- infrastructure/Readme.md | 28 +++- infrastructure/display-api-service/Dockerfile | 72 +++++++-- infrastructure/nginx/Dockerfile | 89 +++++++---- infrastructure/nginx/docker-entrypoint.sh | 49 ------ .../nginx/etc/confd/conf.d/default.conf.toml | 7 - .../nginx/etc/confd/conf.d/nginx.conf.toml | 7 - .../etc/confd/templates/default.conf.tmpl | 75 --------- .../nginx/etc/confd/templates/nginx.conf.tmpl | 52 ------- infrastructure/nginx/etc/nginx.conf | 77 ++++++++++ .../nginx/etc/templates/default.conf.template | 145 ++++++++++++++++++ 11 files changed, 365 insertions(+), 249 deletions(-) delete mode 100644 infrastructure/nginx/docker-entrypoint.sh delete mode 100644 infrastructure/nginx/etc/confd/conf.d/default.conf.toml delete mode 100644 infrastructure/nginx/etc/confd/conf.d/nginx.conf.toml delete mode 100644 infrastructure/nginx/etc/confd/templates/default.conf.tmpl delete mode 100644 infrastructure/nginx/etc/confd/templates/nginx.conf.tmpl create mode 100644 infrastructure/nginx/etc/nginx.conf create mode 100644 infrastructure/nginx/etc/templates/default.conf.template diff --git a/.dockerignore b/.dockerignore index 7e18bc03c..fc10ce7dc 100644 --- a/.dockerignore +++ b/.dockerignore @@ -9,9 +9,7 @@ /infrastructure /public/build /public/fixtures -public/build -public/fixtures -public/media/* +/public/media/* !public/media/thumbnail_other.png !public/media/thumbnail_video.png /scripts @@ -43,17 +41,8 @@ public/media/* xdebug.ini launch.json -###> liip/imagine-bundle ### -/public/media/cache/ -###< liip/imagine-bundle ### - -###> phpstan/phpstan ### -phpstan.neon -###< phpstan/phpstan ### - ###> pentatrion/vite-bundle ### /node_modules/ -/public/build/ ###< pentatrion/vite-bundle ### #> Playwright diff --git a/infrastructure/Readme.md b/infrastructure/Readme.md index 17978bc45..739f3172a 100644 --- a/infrastructure/Readme.md +++ b/infrastructure/Readme.md @@ -1,3 +1,27 @@ -# ITK-development image build +# OS2display image build -This folder contains the infrastructure files for building the `os2display/*` images +This folder contains the infrastructure files for building the `os2display/*` images. + +Two images are built: +- `os2display/display-api-service`: The web (php-fpm) application image +- `os2display/display-api-service-nginx`: The API (nginx) server image + +## Github Actions + +Both images are built automatically on push to the `devolp` branch, and on tag creation. They are tagged with the +same version as the application. + +## Docker Hub + +Images are published to https://hub.docker.com/orgs/os2display + +## Building images locally + +A script is provided to build the images locally: `build-n-push.sh` + +## Build process +Both images uses multistage builds, with the first two stages being identical. And the final stage is optimized for +the specific image. + +This is done because both images requires files from both the `npm` and `composer` build stages. And while having a +shared build stage when building locally is possible, it's not feasible when building on Github Actions. diff --git a/infrastructure/display-api-service/Dockerfile b/infrastructure/display-api-service/Dockerfile index f50de55ff..55bc02687 100644 --- a/infrastructure/display-api-service/Dockerfile +++ b/infrastructure/display-api-service/Dockerfile @@ -1,9 +1,35 @@ -######### API backend ######## +######## Clients [Screen|Admin] build ######## +FROM node:24-alpine AS client_app_builder +LABEL maintainer="ITK Dev " + +ARG APP_VERSION="develop" +ARG APP_RELEASE_VERSION="develop" +ARG APP_RELEASE_TIMESTAMP=0 + +ENV APP_CLIENT_PATH=/app +WORKDIR ${APP_CLIENT_PATH} + +# Copy only necessary files for npm install +COPY --from=repository-root package.json package-lock.json vite.config.js ./ + +# Install dependencies +RUN npm ci --no-audit --no-fund + +# Copy source files needed for build +COPY --from=repository-root assets/ ./assets/ +COPY --from=repository-root public/client/ ./public/client/ + +# Build the application with version info +RUN npm run build + + +######### API backend build ######## FROM itkdev/php8.4-fpm:alpine AS api_app_builder LABEL maintainer="ITK Dev " ARG APP_VERSION="develop" -ENV APP_API_PATH=/var/www/html +ENV APP_CLIENT_PATH=/app \ + APP_API_PATH=/var/www/html USER root @@ -17,28 +43,42 @@ USER deploy # Copy only composer files first for better layer caching COPY --chown=deploy:deploy --from=repository-root composer.json composer.lock symfony.lock ${APP_API_PATH}/ -# Install composer packages first (better image layer caching) +# Pre-install composer packages first (better image layer caching) - application code not present so have to be re-run RUN APP_ENV=prod composer install --no-dev -o --classmap-authoritative --no-scripts # Copy application source (needed for build step) COPY --chown=deploy:deploy --from=repository-root ./ ${APP_API_PATH}/ -# Remove frontend apps from api build -RUN rm -rf /assets +# Copy javascript build files. This ensures that vite manifest files are availiable when "composer insatll" +# triggers a "cache:clear" enabling the vite bundle to generate cache configuration files +# @see https://symfony-vite.pentatrion.com/guide/performance.html#caching-configuration-files-%F0%9F%8F%83 +COPY --chown=deploy:deploy --from=client_app_builder ${APP_CLIENT_PATH}/public/build ./public/build -# Install composer packages, which requires a HACK as redis is not available (should be removed later on). -RUN APP_ENV=prod composer install --no-dev -o --classmap-authoritative +# Re-run composer install after application code copied to image to complete install +RUN APP_ENV=prod composer install --no-dev --optimize-autoloader --classmap-authoritative -#### -## Build main application image. -#### - +######## PHP-FPM (API) production image ######## FROM itkdev/php8.4-fpm:alpine LABEL maintainer="ITK Dev " -ENV APP_API_PATH=/var/www/html \ - COMPOSER_VERSION=2 +ENV APP_CLIENT_PATH=/app \ + APP_API_PATH=/var/www/html \ + COMPOSER_VERSION=2 \ + + # OpCache + PHP_OPCACHE_ENABLED=1 \ + PHP_OPCACHE_VALIDATE_TIMESTAMPS=0 \ + PHP_OPCACHE_MAX_ACCELERATED_FILES=20000 \ + PHP_OPCACHE_MEMORY_CONSUMPTION=256 \ + + # FPM pool + PHP_PM_TYPE="dynamic" \ + PHP_PM_MAX_CHILDREN="24" \ + PHP_PM_MAX_REQUESTS="0" \ + PHP_PM_START_SERVERS="5" \ + PHP_PM_MIN_SPARE_SERVERS="5" \ + PHP_PM_MAX_SPARE_SERVERS="10" USER root @@ -53,9 +93,11 @@ RUN chmod +x /usr/local/bin/docker-entrypoint.sh USER deploy +WORKDIR ${APP_API_PATH} + # Install the api application. -COPY --chown=deploy:deploy --from=api_app_builder ${APP_API_PATH} ${APP_API_PATH} -RUN mkdir -p ${APP_API_PATH}/config/secrets +COPY --chown=deploy:deploy --from=api_app_builder ${APP_API_PATH} . +RUN mkdir -p ./config/secrets WORKDIR ${APP_API_PATH} diff --git a/infrastructure/nginx/Dockerfile b/infrastructure/nginx/Dockerfile index 6754aba58..8379e9cc6 100644 --- a/infrastructure/nginx/Dockerfile +++ b/infrastructure/nginx/Dockerfile @@ -1,66 +1,95 @@ -######## Client [Screen|Admin] build ######## +######## Clients [Screen|Admin] build ######## FROM node:24-alpine AS client_app_builder LABEL maintainer="ITK Dev " ARG APP_VERSION="develop" ARG APP_RELEASE_VERSION="develop" ARG APP_RELEASE_TIMESTAMP=0 -ENV APP_CLIENT_PATH=/app +ENV APP_CLIENT_PATH=/app WORKDIR ${APP_CLIENT_PATH} # Copy only necessary files for npm install -COPY --from=repository-root package.json package-lock.json vite.config.js ${APP_CLIENT_PATH}/ +COPY --from=repository-root package.json package-lock.json vite.config.js ./ # Install dependencies RUN npm ci --no-audit --no-fund # Copy source files needed for build -COPY --from=repository-root assets/ ${APP_CLIENT_PATH}/assets/ -COPY --from=repository-root public/client/ ${APP_CLIENT_PATH}/public/client/ -COPY --from=repository-root vite.config.js ${APP_CLIENT_PATH}/ +COPY --from=repository-root assets/ ./assets/ -# Build the application +# Build the application with version info RUN npm run build -# Clean up build dependencies and cache -RUN npm cache clean --force && \ - rm -rf node_modules/.cache && \ - rm -rf /root/.npm +######### API backend build ######## +FROM itkdev/php8.4-fpm:alpine AS api_app_builder +LABEL maintainer="ITK Dev " + +ARG APP_VERSION="develop" +ENV APP_CLIENT_PATH=/app \ + APP_API_PATH=/var/www/html + +USER root + +# Add composer in from the official composer image (also alpine). +COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer + +WORKDIR ${APP_API_PATH} + +USER deploy + +# Copy only composer files first for better layer caching +COPY --chown=deploy:deploy --from=repository-root composer.json composer.lock symfony.lock ${APP_API_PATH}/ + +# Pre-install composer packages first (better image layer caching) - application code not present so have to be re-run +RUN APP_ENV=prod composer install --no-dev -o --classmap-authoritative --no-scripts + +# Copy application source (needed for build step) +COPY --chown=deploy:deploy --from=repository-root ./ ${APP_API_PATH}/ -######## Nginx build ######## +# Copy javascript build files. This ensures that vite manifest files are availiable when "composer insatll" +# triggers a "cache:clear" enabling the vite bundle to generate cache configuration files +# @see https://symfony-vite.pentatrion.com/guide/performance.html#caching-configuration-files-%F0%9F%8F%83 +COPY --chown=deploy:deploy --from=client_app_builder ${APP_CLIENT_PATH}/public/build ./public/build + +# Re-run composer install after application code copied to image to complete install +RUN APP_ENV=prod composer install --no-dev --optimize-autoloader --classmap-authoritative + + + +######## Nginx production image ######## FROM nginxinc/nginx-unprivileged:alpine LABEL maintainer="ITK Dev " + +ARG APP_VERSION="develop" +ARG APP_RELEASE_VERSION="develop" +ARG APP_RELEASE_TIMESTAMP=0 ARG UID=101 ARG GID=101 -ENV APP_CLIENT_PATH=/app -ENV APP_API_PATH=/var/www/html +ENV APP_CLIENT_PATH=/app \ + APP_API_PATH=/var/www/html \ + NGINX_PORT=8080 -USER root +WORKDIR ${APP_API_PATH} +# Create directory and copy built client assets RUN mkdir -p ${APP_API_PATH}/public -COPY --from=client_app_builder ${APP_CLIENT_PATH}/public ${APP_API_PATH}/public +COPY --from=api_app_builder --chown=$UID:0 ${APP_API_PATH}/public ./public -WORKDIR ${APP_API_PATH} - -# Copy configuration. -COPY etc /etc/ +# Copy configuration and entrypoint script +COPY --chown=$UID:0 etc /etc/nginx -# Install configuration template handler -ADD https://github.com/kelseyhightower/confd/releases/download/v0.16.0/confd-0.16.0-linux-amd64 /usr/local/bin/confd -RUN chmod +x /usr/local/bin/confd +# Set proper permissions (files are already owned by $UID:0 from COPY --chown) +RUN chmod -R g+w ${APP_API_PATH} -COPY docker-entrypoint.sh / -RUN chmod +x /docker-entrypoint.sh \ - && chown -R $UID:0 ${APP_API_PATH} \ - && chmod -R g+w ${APP_API_PATH} +# Add health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:${NGINX_PORT}/health || exit 1 USER $UID -EXPOSE 8080 - -ENTRYPOINT [ "/docker-entrypoint.sh" ] +EXPOSE ${NGINX_PORT} CMD ["nginx", "-g", "daemon off;"] diff --git a/infrastructure/nginx/docker-entrypoint.sh b/infrastructure/nginx/docker-entrypoint.sh deleted file mode 100644 index 978fa9f4e..000000000 --- a/infrastructure/nginx/docker-entrypoint.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/sh - -set -eux - -## Run templates with configuration. -/usr/local/bin/confd --onetime --backend env --confdir /etc/confd - -entrypoint_log() { - if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then - echo "$@" - fi -} - -if [ "$1" = "nginx" -o "$1" = "nginx-debug" ]; then - if /usr/bin/find "/docker-entrypoint.d/" -mindepth 1 -maxdepth 1 -type f -print -quit 2>/dev/null | read v; then - entrypoint_log "$0: /docker-entrypoint.d/ is not empty, will attempt to perform configuration" - - entrypoint_log "$0: Looking for shell scripts in /docker-entrypoint.d/" - find "/docker-entrypoint.d/" -follow -type f -print | sort -V | while read -r f; do - case "$f" in - *.envsh) - if [ -x "$f" ]; then - entrypoint_log "$0: Sourcing $f"; - . "$f" - else - # warn on shell scripts without exec bit - entrypoint_log "$0: Ignoring $f, not executable"; - fi - ;; - *.sh) - if [ -x "$f" ]; then - entrypoint_log "$0: Launching $f"; - "$f" - else - # warn on shell scripts without exec bit - entrypoint_log "$0: Ignoring $f, not executable"; - fi - ;; - *) entrypoint_log "$0: Ignoring $f";; - esac - done - - entrypoint_log "$0: Configuration complete; ready for start up" - else - entrypoint_log "$0: No files found in /docker-entrypoint.d/, skipping configuration" - fi -fi - -exec "$@" diff --git a/infrastructure/nginx/etc/confd/conf.d/default.conf.toml b/infrastructure/nginx/etc/confd/conf.d/default.conf.toml deleted file mode 100644 index 185954f0d..000000000 --- a/infrastructure/nginx/etc/confd/conf.d/default.conf.toml +++ /dev/null @@ -1,7 +0,0 @@ -[template] -src = "default.conf.tmpl" -dest = "/etc/nginx/conf.d/default.conf" -mode = "0644" -keys = [ - "/nginx-config" -] diff --git a/infrastructure/nginx/etc/confd/conf.d/nginx.conf.toml b/infrastructure/nginx/etc/confd/conf.d/nginx.conf.toml deleted file mode 100644 index 7c81a661f..000000000 --- a/infrastructure/nginx/etc/confd/conf.d/nginx.conf.toml +++ /dev/null @@ -1,7 +0,0 @@ -[template] -src = "nginx.conf.tmpl" -dest = "/etc/nginx/nginx.conf" -mode = "0644" -keys = [ - "/nginx-config" -] diff --git a/infrastructure/nginx/etc/confd/templates/default.conf.tmpl b/infrastructure/nginx/etc/confd/templates/default.conf.tmpl deleted file mode 100644 index 7fcd10283..000000000 --- a/infrastructure/nginx/etc/confd/templates/default.conf.tmpl +++ /dev/null @@ -1,75 +0,0 @@ -# @see https://symfony.com/doc/current/setup/web_server_configuration.html -server { - listen 8080; - server_name localhost; - root /var/www/html/public; - - location / { - add_header X-Robots-Tag "noindex, nofollow, nosnippet, noarchive"; - - # try to serve file directly, fallback to index.php - try_files $uri /index.php$is_args$args; - } - - # Cache static assets built by Vite under /public/build - location /build/ { - access_log off; - log_not_found off; - - # Serve files directly - try_files $uri =404; - - # Long-term caching for fingerprinted assets - add_header Cache-Control "public, max-age=31536000, immutable"; - expires 1y; - - # Also allow CORS for fonts and similar assets if needed - add_header Access-Control-Allow-Origin "*" always; - - # Do not set X-Robots-Tag for assets - } - - # Screen client online check should just serve static files - location /client/online-check { - index index.html index.htm; - } - - location = /robots.txt { - add_header Content-Type text/plain; - add_header X-Robots-Tag "noindex, nofollow, nosnippet, noarchive"; - return 200 "User-agent: *\nDisallow: /\n"; - } - - location ~ ^/index\.php(/|$) { - fastcgi_pass {{ getenv "PHP_FPM_SERVER" "phpfpm" }}:{{ getenv "PHP_FPM_SERVER_PORT" "9000" }}; - fastcgi_split_path_info ^(.+\.php)(/.*)$; - include fastcgi_params; - - fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; - fastcgi_param DOCUMENT_ROOT $realpath_root; - - internal; - } - - # return 404 for all other php files not matching the front controller - # this prevents access to other php files you don't want to be accessible. - location ~ \.php$ { - return 404; - } - - error_log /var/log/nginx/error.log; - access_log /var/log/nginx/access.log; -} - -server { - listen 8080; - server_name localhost; - root /var/www/html/public; - - error_log off; - access_log off; - - location /stub_status { - stub_status; - } -} diff --git a/infrastructure/nginx/etc/confd/templates/nginx.conf.tmpl b/infrastructure/nginx/etc/confd/templates/nginx.conf.tmpl deleted file mode 100644 index 29d5283af..000000000 --- a/infrastructure/nginx/etc/confd/templates/nginx.conf.tmpl +++ /dev/null @@ -1,52 +0,0 @@ -worker_processes auto; - -error_log /var/log/nginx/error.log warn; -pid /tmp/nginx.pid; - -events { - worker_connections 2048; - multi_accept on; -} - - -http { - open_file_cache max=10000 inactive=5m; - open_file_cache_valid 5m; - open_file_cache_min_uses 5; - open_file_cache_errors off; - - sendfile on; - tcp_nopush on; - tcp_nodelay on; - keepalive_timeout 15 15; - types_hash_max_size 2048; - - server_tokens off; - - client_max_body_size {{ getenv "NGINX_FPM_UPLOAD_MAX" "8M" }}; - - gzip on; - gzip_disable "msie6"; - gzip_vary on; - gzip_proxied any; - gzip_comp_level 6; - gzip_buffers 16 8k; - gzip_http_version 1.0; - gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript; - - include /etc/nginx/mime.types; - default_type application/octet-stream; - - set_real_ip_from 172.16.0.0/8; - real_ip_recursive on; - real_ip_header X-Forwarded-For; - - log_format main '$http_x_real_ip - $remote_user [$time_local] "$request" ' - '$status $body_bytes_sent "$http_referer" ' - '"$http_user_agent" "$http_x_forwarded_for"'; - - error_log /dev/stderr; - access_log /dev/stdout main; - - include /etc/nginx/conf.d/*.conf; -} diff --git a/infrastructure/nginx/etc/nginx.conf b/infrastructure/nginx/etc/nginx.conf new file mode 100644 index 000000000..2fc6396f0 --- /dev/null +++ b/infrastructure/nginx/etc/nginx.conf @@ -0,0 +1,77 @@ +# Note: 'user' directive is not needed in nginx-unprivileged image +worker_processes auto; +worker_rlimit_nofile 65535; + +error_log /dev/stderr notice; +pid /tmp/nginx.pid; + +events { + worker_connections 2048; + use epoll; + multi_accept on; +} + +http { + open_file_cache max=10000 inactive=5m; + open_file_cache_valid 5m; + open_file_cache_min_uses 5; + open_file_cache_errors off; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 15 15; + types_hash_max_size 2048; + + server_tokens off; + + # Compression + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_comp_level 6; + gzip_buffers 16 8k; + gzip_http_version 1.1; + gzip_types + text/plain + text/css + text/xml + text/javascript + application/javascript + application/x-javascript + application/json + application/xml + application/xml+rss + application/rss+xml + application/atom+xml + image/svg+xml + font/truetype + font/opentype + application/vnd.ms-fontobject + application/font-woff + application/font-woff2; + gzip_disable "msie6"; + gzip_proxied any; + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always; + + # Rate limiting zones + limit_req_zone $binary_remote_addr zone=php_limit:10m rate=10r/s; + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$http_x_real_ip - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + error_log /dev/stderr; + access_log /dev/stdout main; + + include /etc/nginx/conf.d/*.conf; +} diff --git a/infrastructure/nginx/etc/templates/default.conf.template b/infrastructure/nginx/etc/templates/default.conf.template new file mode 100644 index 000000000..1d8f03444 --- /dev/null +++ b/infrastructure/nginx/etc/templates/default.conf.template @@ -0,0 +1,145 @@ +upstream api_backend { + server ${NGINX_FPM_SERVICE}; + keepalive 16; +} + +server { + listen ${NGINX_PORT}; + listen [::]:${NGINX_PORT}; + server_name localhost; + + root /var/www/html/public; + index index.php index.html; + + # Health check endpoint + location /health { + access_log off; + add_header Content-Type text/plain; + return 200 "healthy\n"; + } + + # Upload limits + client_max_body_size ${NGINX_MAX_BODY_SIZE}; + + # Security headers + add_header X-Content-Type-Options "nosniff" always; + add_header X-Frame-Options "DENY" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + + # Real IP from reverse proxy + set_real_ip_from 172.16.0.0/16; + set_real_ip_from ${NGINX_SET_REAL_IP_FROM}; + real_ip_recursive on; + real_ip_header X-Forwarded-For; + + location / { + # Prevent indexing of API routes + add_header X-Robots-Tag "noindex, nofollow, nosnippet, noarchive"; + + # try to serve file directly, fallback to index.php + try_files $uri /index.php$is_args$args; + } + + # Cache static assets built by Vite under /public/build + location /build/ { + access_log off; + log_not_found off; + + # Serve files directly + try_files $uri =404; + + # Long-term caching for fingerprinted assets + add_header Cache-Control "public, max-age=31536000, immutable" always; + add_header Access-Control-Allow-Origin "*" always; + add_header X-Robots-Tag "noindex, nofollow, nosnippet, noarchive" always; + expires 1y; + } + + # Static files with caching + location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + access_log off; + } + + # Screen client online check should just serve static files + location /client/online-check { + index index.html index.htm; + } + + location = /robots.txt { + add_header Content-Type text/plain; + add_header X-Robots-Tag "noindex, nofollow, nosnippet, noarchive"; + return 200 "User-agent: *\nDisallow: /\n"; + } + + # Protect sensitive file extensions + location ~* \.(engine|inc|install|make|module|profile|po|sh|sql|tar|gz|bz2|theme|twig|tpl|xtmpl|yml)$ { + deny all; + return 404; + } + + # Protect backup and temporary files + location ~* \.(bak|orig|save|sw[op])$ { + deny all; + return 404; + } + + # Protect dotfiles except .well-known + location ~ /\.(?!well-known) { + deny all; + return 404; + } + + # Protect specific files + location ~ ^/(composer\.(json|lock)|web\.config)$ { + deny all; + return 404; + } + + # PHP-FPM handling + location ~ ^/index\.php(/|$) { + fastcgi_pass api_backend; + fastcgi_split_path_info ^(.+\.php)(/.*)$; + include fastcgi_params; + + limit_req zone=php_limit burst=20 nodelay; + + # Increase buffer sizes for large responses + fastcgi_buffers 16 32k; + fastcgi_buffer_size 64k; + fastcgi_busy_buffers_size 64k; + + # Timeout settings + fastcgi_connect_timeout 60s; + fastcgi_send_timeout 60s; + fastcgi_read_timeout 60s; + + fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; + fastcgi_param DOCUMENT_ROOT $realpath_root; + + internal; + } + + # return 404 for all other php files not matching the front controller + # this prevents access to other php files you don't want to be accessible. + location ~ \.php$ { + return 404; + } + + # Send log message to files symlinked to stdout/stderr. + sendfile on; + keepalive_timeout 65; + + # Client buffer settings + client_body_buffer_size 128k; + client_header_buffer_size 1k; + large_client_header_buffers 4 16k; + + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml; + gzip_disable "msie6"; +} From 9eb1b411a0488701f997767e2ecebc366587d7c3 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Mon, 8 Dec 2025 08:50:00 +0100 Subject: [PATCH 08/59] 5402: Update Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fa3de154..55e641178 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ All notable changes to this project will be documented in this file. - Fix data fetching bug - Refactored screen layout commands. - Moved list components (search and checkboxes) around +- Updated infrastructure and image build for mono-repo ### NB! Prior to 3.x the project was split into separate repositories From a4e710ab52ae4a96bd96c7fe75a7ce44c87d9754 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Mon, 26 Jan 2026 09:23:49 +0100 Subject: [PATCH 09/59] Random --- .env | 4 ++-- infrastructure/display-api-service/Dockerfile | 1 + infrastructure/nginx/Dockerfile | 4 +++- infrastructure/nginx/etc/templates/default.conf.template | 1 - 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.env b/.env index c8ead6f1d..05d1c0e65 100644 --- a/.env +++ b/.env @@ -19,7 +19,7 @@ ITKDEV_TEMPLATE=symfony-6 ###> symfony/framework-bundle ### APP_ENV=dev -APP_SECRET=22d8a60c047b96413b3337e3ddae3da9 +APP_SECRET=CHANGE_ME TRUSTED_PROXIES=127.0.0.1,REMOTE_ADDR ###< symfony/framework-bundle ### @@ -42,7 +42,7 @@ CORS_ALLOW_ORIGIN='^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$' ###> lexik/jwt-authentication-bundle ### JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem -JWT_PASSPHRASE=APP_JWT_PASSPHRASE +JWT_PASSPHRASE=CHANGE_ME # Default: 1 hour JWT_TOKEN_TTL=3600 # Default: 15 days diff --git a/infrastructure/display-api-service/Dockerfile b/infrastructure/display-api-service/Dockerfile index 55bc02687..748268a43 100644 --- a/infrastructure/display-api-service/Dockerfile +++ b/infrastructure/display-api-service/Dockerfile @@ -64,6 +64,7 @@ LABEL maintainer="ITK Dev " ENV APP_CLIENT_PATH=/app \ APP_API_PATH=/var/www/html \ + APP_ENV=prod \ COMPOSER_VERSION=2 \ # OpCache diff --git a/infrastructure/nginx/Dockerfile b/infrastructure/nginx/Dockerfile index 8379e9cc6..5bf5c00f7 100644 --- a/infrastructure/nginx/Dockerfile +++ b/infrastructure/nginx/Dockerfile @@ -70,7 +70,9 @@ ARG GID=101 ENV APP_CLIENT_PATH=/app \ APP_API_PATH=/var/www/html \ - NGINX_PORT=8080 + NGINX_PORT=8080 \ + NGINX_SET_REAL_IP_FROM=172.17.0.0/16 \ + NGINX_MAX_BODY_SIZE=10m WORKDIR ${APP_API_PATH} diff --git a/infrastructure/nginx/etc/templates/default.conf.template b/infrastructure/nginx/etc/templates/default.conf.template index 1d8f03444..52269bea8 100644 --- a/infrastructure/nginx/etc/templates/default.conf.template +++ b/infrastructure/nginx/etc/templates/default.conf.template @@ -27,7 +27,6 @@ server { add_header Referrer-Policy "strict-origin-when-cross-origin" always; # Real IP from reverse proxy - set_real_ip_from 172.16.0.0/16; set_real_ip_from ${NGINX_SET_REAL_IP_FROM}; real_ip_recursive on; real_ip_header X-Forwarded-For; From 0d0be155dc26ba71a092da8f328e4d86fafd6a8b Mon Sep 17 00:00:00 2001 From: turegjorup Date: Mon, 27 Apr 2026 16:29:58 +0200 Subject: [PATCH 10/59] 5402: Dockerize remaining PHP workflows and align on PHP 8.4 Replace shivammathur/setup-php with docker compose runs against the phpfpm service so all PHP CI runs match the local docker setup (itkdev/php8.4-fpm). phpunit and doctrine now reuse compose's mariadb instead of GitHub services. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/apispec.yaml | 32 ++++---------- .github/workflows/composer_install.yaml | 36 ++++------------ .github/workflows/doctrine.yaml | 51 +++++------------------ .github/workflows/phpunit.yaml | 55 +++++-------------------- .github/workflows/psalm.yaml | 32 ++++---------- .github/workflows/rector.yaml | 32 ++++---------- 6 files changed, 53 insertions(+), 185 deletions(-) diff --git a/.github/workflows/apispec.yaml b/.github/workflows/apispec.yaml index 81de9fed9..cf3c3eac7 100644 --- a/.github/workflows/apispec.yaml +++ b/.github/workflows/apispec.yaml @@ -2,49 +2,33 @@ on: pull_request name: Api Spec +env: + COMPOSE_USER: runner + jobs: apispec: runs-on: ubuntu-latest name: API Specification validation - strategy: - fail-fast: false - matrix: - php: ["8.3"] steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 2 - - name: Setup PHP, with composer and extensions - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php}} - extensions: apcu, ctype, iconv, imagick, json, redis, soap, xmlreader, zip - coverage: none - - - name: Get composer cache directory - id: composer-cache - run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - - - name: Cache composer dependencies - uses: actions/cache@v4 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ matrix.php }}-composer- + - name: Setup network + run: docker network create frontend - name: Install Dependencies - run: composer install -q --no-ansi --no-interaction --no-scripts --no-suggest --no-progress --prefer-dist + run: docker compose run --rm phpfpm composer install - name: Export specifications (yaml) - run: bin/console api:openapi:export --yaml --output=public/api-spec-v2.yaml --no-interaction + run: docker compose run --rm phpfpm bin/console api:openapi:export --yaml --output=public/api-spec-v2.yaml --no-interaction - name: Check for changes in specifications (yaml) run: git diff --diff-filter=ACMRT --exit-code public/api-spec-v2.yaml - name: Export specifications (json) - run: bin/console api:openapi:export --output=public/api-spec-v2.json --no-interaction + run: docker compose run --rm phpfpm bin/console api:openapi:export --output=public/api-spec-v2.json --no-interaction - name: Check for changes in specifications (json) run: git diff --diff-filter=ACMRT --exit-code public/api-spec-v2.json diff --git a/.github/workflows/composer_install.yaml b/.github/workflows/composer_install.yaml index 569a71b32..cc69d8b13 100644 --- a/.github/workflows/composer_install.yaml +++ b/.github/workflows/composer_install.yaml @@ -2,39 +2,19 @@ on: pull_request name: Composer install +env: + COMPOSE_USER: runner + jobs: test-composer-install: runs-on: ubuntu-latest - env: - COMPOSER_ALLOW_SUPERUSER: 1 - strategy: - fail-fast: false - matrix: - php: ["8.3"] - name: Composer install in prod mode (PHP ${{ matrix.php}}) + name: Composer install in prod mode steps: - name: Checkout uses: actions/checkout@v4 - - name: Setup PHP, with composer and extensions - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php}} - extensions: apcu, ctype, iconv, imagick, json, redis, soap, xmlreader, zip - coverage: none - - - name: Get composer cache directory - id: composer-cache - run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - - - name: Cache composer dependencies - uses: actions/cache@v4 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ matrix.php }}-composer- + - name: Setup network + run: docker network create frontend - - name: "[prod] Composer install with exported .env variables" - run: | - set -a && source .env && set +a - APP_ENV=prod composer install --no-dev -o + - name: "[prod] Composer install" + run: docker compose run --rm -e APP_ENV=prod phpfpm composer install --no-dev -o diff --git a/.github/workflows/doctrine.yaml b/.github/workflows/doctrine.yaml index 411acc50f..c24edb83e 100644 --- a/.github/workflows/doctrine.yaml +++ b/.github/workflows/doctrine.yaml @@ -2,56 +2,25 @@ on: pull_request name: Doctrine +env: + COMPOSE_USER: runner + jobs: validate-doctrine-shema: runs-on: ubuntu-latest - env: - DATABASE_URL: mysql://db:db@127.0.0.1:3306/db?serverVersion=mariadb-10.5.13 - strategy: - fail-fast: false - matrix: - php: ["8.3"] - name: Validate Schema (PHP ${{ matrix.php}}) - services: - mariadb: - image: mariadb:10.5.13 - env: - MYSQL_USER: db - MYSQL_PASSWORD: db - MYSQL_DATABASE: db - MYSQL_ROOT_PASSWORD: db - ports: - - 3306:3306 - options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + name: Validate Schema steps: - name: Checkout uses: actions/checkout@v4 - - name: Setup PHP, with composer and extensions - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php}} - extensions: apcu, ctype, iconv, imagick, json, redis, soap, xmlreader, zip - coverage: none - - - name: Get composer cache directory - id: composer-cache - run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - - - name: Cache composer dependencies - uses: actions/cache@v4 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ matrix.php }}-composer- + - name: Setup network + run: docker network create frontend - - name: "Composer install with exported .env variables" - run: | - set -a && source .env && set +a - APP_ENV=prod composer install --no-dev -o + - name: "[prod] Composer install" + run: docker compose run --rm -e APP_ENV=prod phpfpm composer install --no-dev -o - name: Run Doctrine Migrations - run: APP_ENV=prod php bin/console doctrine:migrations:migrate --no-interaction + run: docker compose run --rm -e APP_ENV=prod phpfpm bin/console doctrine:migrations:migrate --no-interaction - name: Validate Doctrine schema - run: APP_ENV=prod php bin/console doctrine:schema:validate + run: docker compose run --rm -e APP_ENV=prod phpfpm bin/console doctrine:schema:validate diff --git a/.github/workflows/phpunit.yaml b/.github/workflows/phpunit.yaml index 2a2e54889..a79440dd9 100644 --- a/.github/workflows/phpunit.yaml +++ b/.github/workflows/phpunit.yaml @@ -2,58 +2,25 @@ on: pull_request name: Test +env: + COMPOSE_USER: runner + jobs: phpunit: runs-on: ubuntu-latest - services: - mariadb: - image: mariadb:lts - ports: - - 3306 - env: - MYSQL_USER: db - MYSQL_PASSWORD: db - MYSQL_DATABASE: db_test - MYSQL_ROOT_PASSWORD: password - # https://mariadb.org/mariadb-server-docker-official-images-healthcheck-without-mysqladmin/ - options: >- - --health-cmd="healthcheck.sh --connect --innodb_initialized" - --health-interval=5s - --health-timeout=2s - --health-retries=3 - strategy: - fail-fast: false - matrix: - php: ["8.3"] - name: PHP Unit tests (PHP ${{ matrix.php }}) + name: PHP Unit tests steps: - name: Checkout uses: actions/checkout@v4 - - name: Setup PHP, with composer and extensions - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php}} - extensions: apcu, ctype, iconv, imagick, json, redis, soap, xmlreader, zip - coverage: none - - - name: Get composer cache directory - id: composer-cache - run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - - - name: Cache composer dependencies - uses: actions/cache@v4 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ matrix.php }}-composer- + - name: Setup network + run: docker network create frontend - name: Install Dependencies - run: composer install -q --no-ansi --no-interaction --no-scripts --no-suggest --no-progress --prefer-dist + run: docker compose run --rm phpfpm composer install - name: PHP Unit - Test setup - env: - PORT: ${{ job.services.mariadb.ports[3306] }} - run: | - DATABASE_URL="mysql://db:db@127.0.0.1:$PORT/db_test" composer run test-setup - DATABASE_URL="mysql://db:db@127.0.0.1:$PORT/db_test" composer run test + run: docker compose run --rm phpfpm composer run test-setup + + - name: PHP Unit - Run tests + run: docker compose run --rm phpfpm composer run test diff --git a/.github/workflows/psalm.yaml b/.github/workflows/psalm.yaml index 6cbb222c9..65785a415 100644 --- a/.github/workflows/psalm.yaml +++ b/.github/workflows/psalm.yaml @@ -2,38 +2,22 @@ on: pull_request name: Psalm +env: + COMPOSE_USER: runner + jobs: psalm: runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - php: ["8.3"] - name: Psalm (PHP ${{ matrix.php }}) + name: Psalm steps: - name: Checkout uses: actions/checkout@v4 - - name: Setup PHP, with composer and extensions - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php}} - extensions: apcu, ctype, iconv, imagick, json, redis, soap, xmlreader, zip - coverage: none - - - name: Get composer cache directory - id: composer-cache - run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - - - name: Cache composer dependencies - uses: actions/cache@v4 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ matrix.php }}-composer- + - name: Setup network + run: docker network create frontend - name: Install Dependencies - run: composer install -q --no-ansi --no-interaction --no-scripts --no-suggest --no-progress --prefer-dist + run: docker compose run --rm phpfpm composer install - name: Psalm - run: phpdbg -qrr ./vendor/bin/psalm + run: docker compose run --rm phpfpm vendor/bin/psalm diff --git a/.github/workflows/rector.yaml b/.github/workflows/rector.yaml index aa482e70b..68ab15301 100644 --- a/.github/workflows/rector.yaml +++ b/.github/workflows/rector.yaml @@ -2,38 +2,22 @@ on: pull_request name: Rector +env: + COMPOSE_USER: runner + jobs: rector: runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - php: ["8.3"] - name: Rector (PHP ${{ matrix.php }}) + name: Rector steps: - name: Checkout uses: actions/checkout@v4 - - name: Setup PHP, with composer and extensions - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php}} - extensions: apcu, ctype, iconv, imagick, json, redis, soap, xmlreader, zip - coverage: none - - - name: Get composer cache directory - id: composer-cache - run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - - - name: Cache composer dependencies - uses: actions/cache@v4 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ matrix.php }}-composer- + - name: Setup network + run: docker network create frontend - name: Install Dependencies - run: composer install -q --no-ansi --no-interaction --no-scripts --no-suggest --no-progress --prefer-dist + run: docker compose run --rm phpfpm composer install - name: Rector - run: phpdbg -qrr ./vendor/bin/rector --dry-run + run: docker compose run --rm phpfpm vendor/bin/rector --dry-run From 91c4bbc7b3607466b0a3cd0a0bd8ee6381e88e67 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Mon, 27 Apr 2026 16:33:14 +0200 Subject: [PATCH 11/59] 5402: Fix yaml and markdown lint findings Apply prettier formatting to github_build_release.yml and pentatrion_vite.yaml; fix list/heading spacing, trailing whitespace and bare URL in infrastructure/Readme.md. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/github_build_release.yml | 2 +- config/packages/pentatrion_vite.yaml | 4 ++-- infrastructure/Readme.md | 8 +++++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/github_build_release.yml b/.github/workflows/github_build_release.yml index 810ab96e3..405dd2593 100644 --- a/.github/workflows/github_build_release.yml +++ b/.github/workflows/github_build_release.yml @@ -24,7 +24,7 @@ jobs: - name: Install, Build, Cleanup run: | task --taskfile=.github/Taskfile.yaml build-prod - + - name: Make assets dir run: | mkdir -p ../assets diff --git a/config/packages/pentatrion_vite.yaml b/config/packages/pentatrion_vite.yaml index c8b11e194..3b3ecaacf 100644 --- a/config/packages/pentatrion_vite.yaml +++ b/config/packages/pentatrion_vite.yaml @@ -1,4 +1,4 @@ # @see https://symfony-vite.pentatrion.com/guide/performance.html#caching-configuration-files-%F0%9F%8F%83 when@prod: - pentatrion_vite: - cache: true + pentatrion_vite: + cache: true diff --git a/infrastructure/Readme.md b/infrastructure/Readme.md index 739f3172a..3d60839e9 100644 --- a/infrastructure/Readme.md +++ b/infrastructure/Readme.md @@ -3,24 +3,26 @@ This folder contains the infrastructure files for building the `os2display/*` images. Two images are built: + - `os2display/display-api-service`: The web (php-fpm) application image - `os2display/display-api-service-nginx`: The API (nginx) server image ## Github Actions -Both images are built automatically on push to the `devolp` branch, and on tag creation. They are tagged with the +Both images are built automatically on push to the `devolp` branch, and on tag creation. They are tagged with the same version as the application. ## Docker Hub -Images are published to https://hub.docker.com/orgs/os2display +Images are published to ## Building images locally A script is provided to build the images locally: `build-n-push.sh` ## Build process -Both images uses multistage builds, with the first two stages being identical. And the final stage is optimized for + +Both images uses multistage builds, with the first two stages being identical. And the final stage is optimized for the specific image. This is done because both images requires files from both the `npm` and `composer` build stages. And while having a From 0ee0f06f42d2fd3a4a9987d41d2db8d102853149 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Mon, 27 Apr 2026 16:48:07 +0200 Subject: [PATCH 12/59] 5402: Roll back PHP 8.4 and Psalm 6 update Revert phpfpm image to itkdev/php8.3-fpm and pin vimeo/psalm back to ^5.12.0. Restore composer.lock and symfony.lock to the release/3.0.0 state. Co-Authored-By: Claude Opus 4.7 (1M context) --- composer.json | 2 +- composer.lock | 1847 ++++++++----------------------------- docker-compose.server.yml | 2 +- docker-compose.yml | 2 +- symfony.lock | 3 + 5 files changed, 409 insertions(+), 1447 deletions(-) diff --git a/composer.json b/composer.json index 30132d0cb..3108dd75c 100644 --- a/composer.json +++ b/composer.json @@ -63,7 +63,7 @@ "symfony/stopwatch": "~6.4.0", "symfony/var-dumper": "~6.4.0", "symfony/web-profiler-bundle": "~6.4.0", - "vimeo/psalm": "^6.13.0", + "vimeo/psalm": "^5.12.0", "vincentlanglet/twig-cs-fixer": "^3.8", "weirdan/doctrine-psalm-plugin": "^2.0" }, diff --git a/composer.lock b/composer.lock index 393d3cd99..846da321c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "567a746ddcb5a440c396fc6ea9cfa7ac", + "content-hash": "1bd6fd456477d1e03a03bb4359ad3e77", "packages": [ { "name": "api-platform/core", @@ -4288,16 +4288,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.6.7", + "version": "5.6.6", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "31a105931bc8ffa3a123383829772e832fd8d903" + "reference": "5cee1d3dfc2d2aa6599834520911d246f656bcb8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/31a105931bc8ffa3a123383829772e832fd8d903", - "reference": "31a105931bc8ffa3a123383829772e832fd8d903", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/5cee1d3dfc2d2aa6599834520911d246f656bcb8", + "reference": "5cee1d3dfc2d2aa6599834520911d246f656bcb8", "shasum": "" }, "require": { @@ -4346,9 +4346,9 @@ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.7" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.6" }, - "time": "2026-03-18T20:47:46+00:00" + "time": "2025-12-22T21:13:58+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -5723,16 +5723,16 @@ }, { "name": "symfony/cache", - "version": "v6.4.36", + "version": "v6.4.34", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "5b94fba945d1f9e7929cffd50e7a17f1ac36f10b" + "reference": "a0a1690543329685c044362c873b78c6de9d4faa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/5b94fba945d1f9e7929cffd50e7a17f1ac36f10b", - "reference": "5b94fba945d1f9e7929cffd50e7a17f1ac36f10b", + "url": "https://api.github.com/repos/symfony/cache/zipball/a0a1690543329685c044362c873b78c6de9d4faa", + "reference": "a0a1690543329685c044362c873b78c6de9d4faa", "shasum": "" }, "require": { @@ -5799,7 +5799,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v6.4.36" + "source": "https://github.com/symfony/cache/tree/v6.4.34" }, "funding": [ { @@ -5819,7 +5819,7 @@ "type": "tidelift" } ], - "time": "2026-03-30T14:52:43+00:00" + "time": "2026-02-20T15:06:30+00:00" }, { "name": "symfony/cache-contracts", @@ -6056,16 +6056,16 @@ }, { "name": "symfony/console", - "version": "v6.4.36", + "version": "v6.4.34", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "9f481cfb580db8bcecc9b2d4c63f3e13df022ad5" + "reference": "7b1f1c37eff5910ddda2831345467e593a5120ad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/9f481cfb580db8bcecc9b2d4c63f3e13df022ad5", - "reference": "9f481cfb580db8bcecc9b2d4c63f3e13df022ad5", + "url": "https://api.github.com/repos/symfony/console/zipball/7b1f1c37eff5910ddda2831345467e593a5120ad", + "reference": "7b1f1c37eff5910ddda2831345467e593a5120ad", "shasum": "" }, "require": { @@ -6130,7 +6130,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.4.36" + "source": "https://github.com/symfony/console/tree/v6.4.34" }, "funding": [ { @@ -6150,20 +6150,20 @@ "type": "tidelift" } ], - "time": "2026-03-27T15:30:51+00:00" + "time": "2026-02-23T15:42:15+00:00" }, { "name": "symfony/dependency-injection", - "version": "v6.4.36", + "version": "v6.4.34", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "cd7881a6dc84b780411199cd0584e1a53a3b9ba7" + "reference": "91e49958b8a6092e48e4711894a1aeb1b151c62a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/cd7881a6dc84b780411199cd0584e1a53a3b9ba7", - "reference": "cd7881a6dc84b780411199cd0584e1a53a3b9ba7", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/91e49958b8a6092e48e4711894a1aeb1b151c62a", + "reference": "91e49958b8a6092e48e4711894a1aeb1b151c62a", "shasum": "" }, "require": { @@ -6215,7 +6215,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v6.4.36" + "source": "https://github.com/symfony/dependency-injection/tree/v6.4.34" }, "funding": [ { @@ -6235,7 +6235,7 @@ "type": "tidelift" } ], - "time": "2026-03-30T16:39:36+00:00" + "time": "2026-02-24T15:33:38+00:00" }, { "name": "symfony/deprecation-contracts", @@ -6567,16 +6567,16 @@ }, { "name": "symfony/error-handler", - "version": "v6.4.36", + "version": "v6.4.32", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "2ea68f0e1835ad6a126f93bbc14cd236c10ab361" + "reference": "8c18400784fcb014dc73c8d5601a9576af7f8ad4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/2ea68f0e1835ad6a126f93bbc14cd236c10ab361", - "reference": "2ea68f0e1835ad6a126f93bbc14cd236c10ab361", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/8c18400784fcb014dc73c8d5601a9576af7f8ad4", + "reference": "8c18400784fcb014dc73c8d5601a9576af7f8ad4", "shasum": "" }, "require": { @@ -6622,7 +6622,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v6.4.36" + "source": "https://github.com/symfony/error-handler/tree/v6.4.32" }, "funding": [ { @@ -6642,20 +6642,20 @@ "type": "tidelift" } ], - "time": "2026-03-10T15:56:14+00:00" + "time": "2026-01-19T19:28:19+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v6.4.36", + "version": "v6.4.32", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "fc828863e26ceec86e2513b5e46aa0b149d76b69" + "reference": "99d7e101826e6610606b9433248f80c1997cd20b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/fc828863e26ceec86e2513b5e46aa0b149d76b69", - "reference": "fc828863e26ceec86e2513b5e46aa0b149d76b69", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/99d7e101826e6610606b9433248f80c1997cd20b", + "reference": "99d7e101826e6610606b9433248f80c1997cd20b", "shasum": "" }, "require": { @@ -6706,7 +6706,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.36" + "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.32" }, "funding": [ { @@ -6726,7 +6726,7 @@ "type": "tidelift" } ], - "time": "2026-03-30T11:18:01+00:00" + "time": "2026-01-05T11:13:48+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -7085,16 +7085,16 @@ }, { "name": "symfony/framework-bundle", - "version": "v6.4.36", + "version": "v6.4.34", "source": { "type": "git", "url": "https://github.com/symfony/framework-bundle.git", - "reference": "147b02cfa45dcc74a290462551f5ee5c7fa8ab17" + "reference": "5b5d19473f22d699811a41b01cef2462bc42b238" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/147b02cfa45dcc74a290462551f5ee5c7fa8ab17", - "reference": "147b02cfa45dcc74a290462551f5ee5c7fa8ab17", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/5b5d19473f22d699811a41b01cef2462bc42b238", + "reference": "5b5d19473f22d699811a41b01cef2462bc42b238", "shasum": "" }, "require": { @@ -7214,7 +7214,7 @@ "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/framework-bundle/tree/v6.4.36" + "source": "https://github.com/symfony/framework-bundle/tree/v6.4.34" }, "funding": [ { @@ -7234,7 +7234,7 @@ "type": "tidelift" } ], - "time": "2026-03-25T17:41:29+00:00" + "time": "2026-02-24T16:00:52+00:00" }, { "name": "symfony/http-client", @@ -7414,16 +7414,16 @@ }, { "name": "symfony/http-foundation", - "version": "v6.4.35", + "version": "v6.4.34", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "cffffd0a2c037117b742b4f8b379a22a2a33f6d2" + "reference": "5bb346d1b4b2a616e5c3d99b3ee4d5810735c535" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/cffffd0a2c037117b742b4f8b379a22a2a33f6d2", - "reference": "cffffd0a2c037117b742b4f8b379a22a2a33f6d2", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/5bb346d1b4b2a616e5c3d99b3ee4d5810735c535", + "reference": "5bb346d1b4b2a616e5c3d99b3ee4d5810735c535", "shasum": "" }, "require": { @@ -7471,7 +7471,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v6.4.35" + "source": "https://github.com/symfony/http-foundation/tree/v6.4.34" }, "funding": [ { @@ -7491,20 +7491,20 @@ "type": "tidelift" } ], - "time": "2026-03-06T11:15:58+00:00" + "time": "2026-02-21T15:48:41+00:00" }, { "name": "symfony/http-kernel", - "version": "v6.4.36", + "version": "v6.4.34", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "4087ec02119de450e9ebb60806d69c6bb8c6e468" + "reference": "006a49fc4f41ee21a6ca61e69caed1c30b29f07c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/4087ec02119de450e9ebb60806d69c6bb8c6e468", - "reference": "4087ec02119de450e9ebb60806d69c6bb8c6e468", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/006a49fc4f41ee21a6ca61e69caed1c30b29f07c", + "reference": "006a49fc4f41ee21a6ca61e69caed1c30b29f07c", "shasum": "" }, "require": { @@ -7589,7 +7589,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v6.4.36" + "source": "https://github.com/symfony/http-kernel/tree/v6.4.34" }, "funding": [ { @@ -7609,20 +7609,20 @@ "type": "tidelift" } ], - "time": "2026-03-31T20:38:11+00:00" + "time": "2026-02-26T08:27:11+00:00" }, { "name": "symfony/mime", - "version": "v6.4.36", + "version": "v6.4.34", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "9c31726137c70798f815fb98293ffb8a2a47694c" + "reference": "2b32fbbe10b36a8379efab6e702ad8b917151839" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/9c31726137c70798f815fb98293ffb8a2a47694c", - "reference": "9c31726137c70798f815fb98293ffb8a2a47694c", + "url": "https://api.github.com/repos/symfony/mime/zipball/2b32fbbe10b36a8379efab6e702ad8b917151839", + "reference": "2b32fbbe10b36a8379efab6e702ad8b917151839", "shasum": "" }, "require": { @@ -7678,7 +7678,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v6.4.36" + "source": "https://github.com/symfony/mime/tree/v6.4.34" }, "funding": [ { @@ -7698,7 +7698,7 @@ "type": "tidelift" } ], - "time": "2026-03-30T09:31:23+00:00" + "time": "2026-02-02T17:01:23+00:00" }, { "name": "symfony/monolog-bridge", @@ -8012,16 +8012,16 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.37.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e" + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/4864388bfbd3001ce88e234fab652acd91fdc57e", - "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", "shasum": "" }, "require": { @@ -8070,7 +8070,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.37.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" }, "funding": [ { @@ -8090,11 +8090,11 @@ "type": "tidelift" } ], - "time": "2026-04-26T13:13:48+00:00" + "time": "2025-06-27T09:58:17+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.37.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", @@ -8157,7 +8157,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.37.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0" }, "funding": [ { @@ -8181,7 +8181,7 @@ }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.37.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -8242,7 +8242,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.37.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" }, "funding": [ { @@ -8266,16 +8266,16 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.37.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315" + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6a21eb99c6973357967f6ce3708cd55a6bec6315", - "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", "shasum": "" }, "require": { @@ -8327,7 +8327,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.37.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" }, "funding": [ { @@ -8347,7 +8347,7 @@ "type": "tidelift" } ], - "time": "2026-04-10T17:25:58+00:00" + "time": "2024-12-23T08:48:59+00:00" }, { "name": "symfony/polyfill-php56", @@ -8419,16 +8419,16 @@ }, { "name": "symfony/polyfill-php80", - "version": "v1.37.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411" + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411", - "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", "shasum": "" }, "require": { @@ -8479,7 +8479,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.37.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" }, "funding": [ { @@ -8499,11 +8499,11 @@ "type": "tidelift" } ], - "time": "2026-04-10T16:19:22+00:00" + "time": "2025-01-02T08:10:11+00:00" }, { "name": "symfony/polyfill-php81", - "version": "v1.37.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php81.git", @@ -8559,7 +8559,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.37.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.33.0" }, "funding": [ { @@ -8583,16 +8583,16 @@ }, { "name": "symfony/polyfill-php83", - "version": "v1.37.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "3600c2cb22399e25bb226e4a135ce91eeb2a6149" + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/3600c2cb22399e25bb226e4a135ce91eeb2a6149", - "reference": "3600c2cb22399e25bb226e4a135ce91eeb2a6149", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5", "shasum": "" }, "require": { @@ -8639,7 +8639,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.37.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0" }, "funding": [ { @@ -8659,20 +8659,20 @@ "type": "tidelift" } ], - "time": "2026-04-10T17:25:58+00:00" + "time": "2025-07-08T02:45:35+00:00" }, { "name": "symfony/polyfill-php84", - "version": "v1.37.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php84.git", - "reference": "88486db2c389b290bf87ff1de7ebc1e13e42bb06" + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/88486db2c389b290bf87ff1de7ebc1e13e42bb06", - "reference": "88486db2c389b290bf87ff1de7ebc1e13e42bb06", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", "shasum": "" }, "require": { @@ -8719,7 +8719,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php84/tree/v1.37.0" + "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0" }, "funding": [ { @@ -8739,7 +8739,7 @@ "type": "tidelift" } ], - "time": "2026-04-10T18:47:49+00:00" + "time": "2025-06-24T13:30:11+00:00" }, { "name": "symfony/polyfill-uuid", @@ -10479,16 +10479,16 @@ }, { "name": "symfony/var-dumper", - "version": "v6.4.36", + "version": "v6.4.32", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "7c8ad9ce4faf6c8a99948e70ce02b601a0439782" + "reference": "131fc9915e0343052af5ed5040401b481ca192aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/7c8ad9ce4faf6c8a99948e70ce02b601a0439782", - "reference": "7c8ad9ce4faf6c8a99948e70ce02b601a0439782", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/131fc9915e0343052af5ed5040401b481ca192aa", + "reference": "131fc9915e0343052af5ed5040401b481ca192aa", "shasum": "" }, "require": { @@ -10543,7 +10543,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.4.36" + "source": "https://github.com/symfony/var-dumper/tree/v6.4.32" }, "funding": [ { @@ -10563,20 +10563,20 @@ "type": "tidelift" } ], - "time": "2026-03-30T15:36:00+00:00" + "time": "2026-01-01T13:34:06+00:00" }, { "name": "symfony/var-exporter", - "version": "v6.4.36", + "version": "v6.4.26", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "f9c4a9695a9e2bbc65c920e147d8d7ae28f8d79a" + "reference": "466fcac5fa2e871f83d31173f80e9c2684743bfc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/f9c4a9695a9e2bbc65c920e147d8d7ae28f8d79a", - "reference": "f9c4a9695a9e2bbc65c920e147d8d7ae28f8d79a", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/466fcac5fa2e871f83d31173f80e9c2684743bfc", + "reference": "466fcac5fa2e871f83d31173f80e9c2684743bfc", "shasum": "" }, "require": { @@ -10624,7 +10624,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v6.4.36" + "source": "https://github.com/symfony/var-exporter/tree/v6.4.26" }, "funding": [ { @@ -10644,7 +10644,7 @@ "type": "tidelift" } ], - "time": "2026-03-10T15:06:19+00:00" + "time": "2025-09-11T09:57:09+00:00" }, { "name": "symfony/web-link", @@ -11114,36 +11114,38 @@ "packages-dev": [ { "name": "amphp/amp", - "version": "v3.1.1", + "version": "v2.6.5", "source": { "type": "git", "url": "https://github.com/amphp/amp.git", - "reference": "fa0ab33a6f47a82929c38d03ca47ebb71086a93f" + "reference": "d7dda98dae26e56f3f6fcfbf1c1f819c9a993207" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amphp/amp/zipball/fa0ab33a6f47a82929c38d03ca47ebb71086a93f", - "reference": "fa0ab33a6f47a82929c38d03ca47ebb71086a93f", + "url": "https://api.github.com/repos/amphp/amp/zipball/d7dda98dae26e56f3f6fcfbf1c1f819c9a993207", + "reference": "d7dda98dae26e56f3f6fcfbf1c1f819c9a993207", "shasum": "" }, "require": { - "php": ">=8.1", - "revolt/event-loop": "^1 || ^0.2" + "php": ">=7.1" }, "require-dev": { - "amphp/php-cs-fixer-config": "^2", - "phpunit/phpunit": "^9", - "psalm/phar": "5.23.1" + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1", + "ext-json": "*", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^7 | ^8 | ^9", + "react/promise": "^2", + "vimeo/psalm": "^3.12" }, "type": "library", "autoload": { "files": [ - "src/functions.php", - "src/Future/functions.php", - "src/Internal/functions.php" + "lib/functions.php", + "lib/Internal/functions.php" ], "psr-4": { - "Amp\\": "src" + "Amp\\": "lib" } }, "notification-url": "https://packagist.org/downloads/", @@ -11151,6 +11153,10 @@ "MIT" ], "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, { "name": "Aaron Piotrowski", "email": "aaron@trowski.com" @@ -11162,10 +11168,6 @@ { "name": "Niklas Keller", "email": "me@kelunik.com" - }, - { - "name": "Daniel Lowrey", - "email": "rdlowrey@php.net" } ], "description": "A non-blocking concurrency framework for PHP applications.", @@ -11182,8 +11184,9 @@ "promise" ], "support": { + "irc": "irc://irc.freenode.org/amphp", "issues": "https://github.com/amphp/amp/issues", - "source": "https://github.com/amphp/amp/tree/v3.1.1" + "source": "https://github.com/amphp/amp/tree/v2.6.5" }, "funding": [ { @@ -11191,45 +11194,41 @@ "type": "github" } ], - "time": "2025-08-27T21:42:00+00:00" + "time": "2025-09-03T19:41:28+00:00" }, { "name": "amphp/byte-stream", - "version": "v2.1.2", + "version": "v1.8.2", "source": { "type": "git", "url": "https://github.com/amphp/byte-stream.git", - "reference": "55a6bd071aec26fa2a3e002618c20c35e3df1b46" + "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amphp/byte-stream/zipball/55a6bd071aec26fa2a3e002618c20c35e3df1b46", - "reference": "55a6bd071aec26fa2a3e002618c20c35e3df1b46", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/4f0e968ba3798a423730f567b1b50d3441c16ddc", + "reference": "4f0e968ba3798a423730f567b1b50d3441c16ddc", "shasum": "" }, "require": { - "amphp/amp": "^3", - "amphp/parser": "^1.1", - "amphp/pipeline": "^1", - "amphp/serialization": "^1", - "amphp/sync": "^2", - "php": ">=8.1", - "revolt/event-loop": "^1 || ^0.2.3" + "amphp/amp": "^2", + "php": ">=7.1" }, "require-dev": { - "amphp/php-cs-fixer-config": "^2", - "amphp/phpunit-util": "^3", - "phpunit/phpunit": "^9", - "psalm/phar": "5.22.1" + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1.4", + "friendsofphp/php-cs-fixer": "^2.3", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^6 || ^7 || ^8", + "psalm/phar": "^3.11.4" }, "type": "library", "autoload": { "files": [ - "src/functions.php", - "src/Internal/functions.php" + "lib/functions.php" ], "psr-4": { - "Amp\\ByteStream\\": "src" + "Amp\\ByteStream\\": "lib" } }, "notification-url": "https://packagist.org/downloads/", @@ -11258,7 +11257,7 @@ ], "support": { "issues": "https://github.com/amphp/byte-stream/issues", - "source": "https://github.com/amphp/byte-stream/tree/v2.1.2" + "source": "https://github.com/amphp/byte-stream/tree/v1.8.2" }, "funding": [ { @@ -11266,112 +11265,94 @@ "type": "github" } ], - "time": "2025-03-16T17:10:27+00:00" + "time": "2024-04-13T18:00:56+00:00" }, { - "name": "amphp/cache", - "version": "v2.0.1", + "name": "dnoegel/php-xdg-base-dir", + "version": "v0.1.1", "source": { "type": "git", - "url": "https://github.com/amphp/cache.git", - "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c" + "url": "https://github.com/dnoegel/php-xdg-base-dir.git", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amphp/cache/zipball/46912e387e6aa94933b61ea1ead9cf7540b7797c", - "reference": "46912e387e6aa94933b61ea1ead9cf7540b7797c", + "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", "shasum": "" }, "require": { - "amphp/amp": "^3", - "amphp/serialization": "^1", - "amphp/sync": "^2", - "php": ">=8.1", - "revolt/event-loop": "^1 || ^0.2" + "php": ">=5.3.2" }, "require-dev": { - "amphp/php-cs-fixer-config": "^2", - "amphp/phpunit-util": "^3", - "phpunit/phpunit": "^9", - "psalm/phar": "^5.4" + "phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35" }, "type": "library", "autoload": { "psr-4": { - "Amp\\Cache\\": "src" + "XdgBaseDir\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - }, - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Daniel Lowrey", - "email": "rdlowrey@php.net" - } - ], - "description": "A fiber-aware cache API based on Amp and Revolt.", - "homepage": "https://amphp.org/cache", + "description": "implementation of xdg base directory specification for php", "support": { - "issues": "https://github.com/amphp/cache/issues", - "source": "https://github.com/amphp/cache/tree/v2.0.1" + "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues", + "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1" }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2024-04-19T03:38:06+00:00" + "time": "2019-12-04T15:06:13+00:00" }, { - "name": "amphp/dns", - "version": "v2.4.0", + "name": "doctrine/data-fixtures", + "version": "1.8.2", "source": { "type": "git", - "url": "https://github.com/amphp/dns.git", - "reference": "78eb3db5fc69bf2fc0cb503c4fcba667bc223c71" + "url": "https://github.com/doctrine/data-fixtures.git", + "reference": "6fb221da56dae2011b33d47508e3b8aeb1d91db5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amphp/dns/zipball/78eb3db5fc69bf2fc0cb503c4fcba667bc223c71", - "reference": "78eb3db5fc69bf2fc0cb503c4fcba667bc223c71", + "url": "https://api.github.com/repos/doctrine/data-fixtures/zipball/6fb221da56dae2011b33d47508e3b8aeb1d91db5", + "reference": "6fb221da56dae2011b33d47508e3b8aeb1d91db5", "shasum": "" }, "require": { - "amphp/amp": "^3", - "amphp/byte-stream": "^2", - "amphp/cache": "^2", - "amphp/parser": "^1", - "amphp/process": "^2", - "daverandom/libdns": "^2.0.2", - "ext-filter": "*", - "ext-json": "*", - "php": ">=8.1", - "revolt/event-loop": "^1 || ^0.2" + "doctrine/deprecations": "^0.5.3 || ^1.0", + "doctrine/persistence": "^2.0 || ^3.0", + "php": "^7.4 || ^8.0", + "symfony/polyfill-php80": "^1" + }, + "conflict": { + "doctrine/dbal": "<3.5 || >=5", + "doctrine/orm": "<2.14 || >=4", + "doctrine/phpcr-odm": "<1.3.0" }, "require-dev": { - "amphp/php-cs-fixer-config": "^2", - "amphp/phpunit-util": "^3", - "phpunit/phpunit": "^9", - "psalm/phar": "5.20" + "doctrine/annotations": "^1.12 || ^2", + "doctrine/coding-standard": "^13", + "doctrine/dbal": "^3.5 || ^4", + "doctrine/mongodb-odm": "^1.3.0 || ^2.0.0", + "doctrine/orm": "^2.14 || ^3", + "ext-sqlite3": "*", + "fig/log-test": "^1", + "phpstan/phpstan": "2.1.17", + "phpunit/phpunit": "^9.6.13 || 10.5.45", + "psr/log": "^1.1 || ^2 || ^3", + "symfony/cache": "^5.4 || ^6.3 || ^7", + "symfony/var-exporter": "^5.4 || ^6.3 || ^7" + }, + "suggest": { + "alcaeus/mongo-php-adapter": "For using MongoDB ODM 1.3 with PHP 7 (deprecated)", + "doctrine/mongodb-odm": "For loading MongoDB ODM fixtures", + "doctrine/orm": "For loading ORM fixtures", + "doctrine/phpcr-odm": "For loading PHPCR ODM fixtures" }, "type": "library", "autoload": { - "files": [ - "src/functions.php" - ], "psr-4": { - "Amp\\Dns\\": "src" + "Doctrine\\Common\\DataFixtures\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -11380,91 +11361,91 @@ ], "authors": [ { - "name": "Chris Wright", - "email": "addr@daverandom.com" - }, - { - "name": "Daniel Lowrey", - "email": "rdlowrey@php.net" - }, - { - "name": "Bob Weinand", - "email": "bobwei9@hotmail.com" - }, - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - }, - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" } ], - "description": "Async DNS resolution for Amp.", - "homepage": "https://github.com/amphp/dns", + "description": "Data Fixtures for all Doctrine Object Managers", + "homepage": "https://www.doctrine-project.org", "keywords": [ - "amp", - "amphp", - "async", - "client", - "dns", - "resolve" + "database" ], "support": { - "issues": "https://github.com/amphp/dns/issues", - "source": "https://github.com/amphp/dns/tree/v2.4.0" + "issues": "https://github.com/doctrine/data-fixtures/issues", + "source": "https://github.com/doctrine/data-fixtures/tree/1.8.2" }, "funding": [ { - "url": "https://github.com/amphp", - "type": "github" + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdata-fixtures", + "type": "tidelift" } ], - "time": "2025-01-19T15:43:40+00:00" + "time": "2025-06-10T07:00:05+00:00" }, { - "name": "amphp/parallel", - "version": "v2.3.3", + "name": "ergebnis/composer-normalize", + "version": "2.50.0", "source": { "type": "git", - "url": "https://github.com/amphp/parallel.git", - "reference": "296b521137a54d3a02425b464e5aee4c93db2c60" + "url": "https://github.com/ergebnis/composer-normalize.git", + "reference": "80971fe24ff10709789942bcbe9368b2c704097c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amphp/parallel/zipball/296b521137a54d3a02425b464e5aee4c93db2c60", - "reference": "296b521137a54d3a02425b464e5aee4c93db2c60", + "url": "https://api.github.com/repos/ergebnis/composer-normalize/zipball/80971fe24ff10709789942bcbe9368b2c704097c", + "reference": "80971fe24ff10709789942bcbe9368b2c704097c", "shasum": "" }, "require": { - "amphp/amp": "^3", - "amphp/byte-stream": "^2", - "amphp/cache": "^2", - "amphp/parser": "^1", - "amphp/pipeline": "^1", - "amphp/process": "^2", - "amphp/serialization": "^1", - "amphp/socket": "^2", - "amphp/sync": "^2", - "php": ">=8.1", - "revolt/event-loop": "^1" + "composer-plugin-api": "^2.0.0", + "ergebnis/json": "^1.4.0", + "ergebnis/json-normalizer": "^4.9.0", + "ergebnis/json-printer": "^3.7.0", + "ext-json": "*", + "justinrainbow/json-schema": "^5.2.12 || ^6.0.0", + "localheinz/diff": "^1.3.0", + "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" }, "require-dev": { - "amphp/php-cs-fixer-config": "^2", - "amphp/phpunit-util": "^3", - "phpunit/phpunit": "^9", - "psalm/phar": "^5.18" + "composer/composer": "^2.9.4", + "ergebnis/license": "^2.7.0", + "ergebnis/php-cs-fixer-config": "^6.59.0", + "ergebnis/phpstan-rules": "^2.13.1", + "ergebnis/phpunit-slow-test-detector": "^2.20.0", + "ergebnis/rector-rules": "^1.9.0", + "fakerphp/faker": "^1.24.1", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^2.1.38", + "phpstan/phpstan-deprecation-rules": "^2.0.3", + "phpstan/phpstan-phpunit": "^2.0.12", + "phpstan/phpstan-strict-rules": "^2.0.8", + "phpunit/phpunit": "^9.6.33", + "rector/rector": "^2.3.5", + "symfony/filesystem": "^5.4.41" + }, + "type": "composer-plugin", + "extra": { + "class": "Ergebnis\\Composer\\Normalize\\NormalizePlugin", + "branch-alias": { + "dev-main": "2.49-dev" + }, + "plugin-optional": true, + "composer-normalize": { + "indent-size": 2, + "indent-style": "space" + } }, - "type": "library", "autoload": { - "files": [ - "src/Context/functions.php", - "src/Context/Internal/functions.php", - "src/Ipc/functions.php", - "src/Worker/functions.php" - ], "psr-4": { - "Amp\\Parallel\\": "src" + "Ergebnis\\Composer\\Normalize\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -11473,813 +11454,74 @@ ], "authors": [ { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - }, - { - "name": "Stephen Coakley", - "email": "me@stephencoakley.com" + "name": "Andreas Möller", + "email": "am@localheinz.com", + "homepage": "https://localheinz.com" } ], - "description": "Parallel processing component for Amp.", - "homepage": "https://github.com/amphp/parallel", + "description": "Provides a composer plugin for normalizing composer.json.", + "homepage": "https://github.com/ergebnis/composer-normalize", "keywords": [ - "async", - "asynchronous", - "concurrent", - "multi-processing", - "multi-threading" + "composer", + "normalize", + "normalizer", + "plugin" ], "support": { - "issues": "https://github.com/amphp/parallel/issues", - "source": "https://github.com/amphp/parallel/tree/v2.3.3" + "issues": "https://github.com/ergebnis/composer-normalize/issues", + "security": "https://github.com/ergebnis/composer-normalize/blob/main/.github/SECURITY.md", + "source": "https://github.com/ergebnis/composer-normalize" }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2025-11-15T06:23:42+00:00" + "time": "2026-02-09T20:57:47+00:00" }, { - "name": "amphp/parser", - "version": "v1.1.1", + "name": "ergebnis/json", + "version": "1.6.0", "source": { "type": "git", - "url": "https://github.com/amphp/parser.git", - "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7" + "url": "https://github.com/ergebnis/json.git", + "reference": "7b56d2b5d9e897e75b43e2e753075a0904c921b1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amphp/parser/zipball/3cf1f8b32a0171d4b1bed93d25617637a77cded7", - "reference": "3cf1f8b32a0171d4b1bed93d25617637a77cded7", + "url": "https://api.github.com/repos/ergebnis/json/zipball/7b56d2b5d9e897e75b43e2e753075a0904c921b1", + "reference": "7b56d2b5d9e897e75b43e2e753075a0904c921b1", "shasum": "" }, "require": { - "php": ">=7.4" + "ext-json": "*", + "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" }, "require-dev": { - "amphp/php-cs-fixer-config": "^2", - "phpunit/phpunit": "^9", - "psalm/phar": "^5.4" + "ergebnis/composer-normalize": "^2.44.0", + "ergebnis/data-provider": "^3.3.0", + "ergebnis/license": "^2.5.0", + "ergebnis/php-cs-fixer-config": "^6.37.0", + "ergebnis/phpstan-rules": "^2.11.0", + "ergebnis/phpunit-slow-test-detector": "^2.16.1", + "fakerphp/faker": "^1.24.0", + "infection/infection": "~0.26.6", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^2.1.22", + "phpstan/phpstan-deprecation-rules": "^2.0.3", + "phpstan/phpstan-phpunit": "^2.0.7", + "phpstan/phpstan-strict-rules": "^2.0.6", + "phpunit/phpunit": "^9.6.24", + "rector/rector": "^2.1.4" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.7-dev" + }, + "composer-normalize": { + "indent-size": 2, + "indent-style": "space" + } + }, "autoload": { "psr-4": { - "Amp\\Parser\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - } - ], - "description": "A generator parser to make streaming parsers simple.", - "homepage": "https://github.com/amphp/parser", - "keywords": [ - "async", - "non-blocking", - "parser", - "stream" - ], - "support": { - "issues": "https://github.com/amphp/parser/issues", - "source": "https://github.com/amphp/parser/tree/v1.1.1" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2024-03-21T19:16:53+00:00" - }, - { - "name": "amphp/pipeline", - "version": "v1.2.3", - "source": { - "type": "git", - "url": "https://github.com/amphp/pipeline.git", - "reference": "7b52598c2e9105ebcddf247fc523161581930367" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/pipeline/zipball/7b52598c2e9105ebcddf247fc523161581930367", - "reference": "7b52598c2e9105ebcddf247fc523161581930367", - "shasum": "" - }, - "require": { - "amphp/amp": "^3", - "php": ">=8.1", - "revolt/event-loop": "^1" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "^2", - "amphp/phpunit-util": "^3", - "phpunit/phpunit": "^9", - "psalm/phar": "^5.18" - }, - "type": "library", - "autoload": { - "psr-4": { - "Amp\\Pipeline\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - } - ], - "description": "Asynchronous iterators and operators.", - "homepage": "https://amphp.org/pipeline", - "keywords": [ - "amp", - "amphp", - "async", - "io", - "iterator", - "non-blocking" - ], - "support": { - "issues": "https://github.com/amphp/pipeline/issues", - "source": "https://github.com/amphp/pipeline/tree/v1.2.3" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2025-03-16T16:33:53+00:00" - }, - { - "name": "amphp/process", - "version": "v2.0.3", - "source": { - "type": "git", - "url": "https://github.com/amphp/process.git", - "reference": "52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/process/zipball/52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d", - "reference": "52e08c09dec7511d5fbc1fb00d3e4e79fc77d58d", - "shasum": "" - }, - "require": { - "amphp/amp": "^3", - "amphp/byte-stream": "^2", - "amphp/sync": "^2", - "php": ">=8.1", - "revolt/event-loop": "^1 || ^0.2" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "^2", - "amphp/phpunit-util": "^3", - "phpunit/phpunit": "^9", - "psalm/phar": "^5.4" - }, - "type": "library", - "autoload": { - "files": [ - "src/functions.php" - ], - "psr-4": { - "Amp\\Process\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bob Weinand", - "email": "bobwei9@hotmail.com" - }, - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - } - ], - "description": "A fiber-aware process manager based on Amp and Revolt.", - "homepage": "https://amphp.org/process", - "support": { - "issues": "https://github.com/amphp/process/issues", - "source": "https://github.com/amphp/process/tree/v2.0.3" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2024-04-19T03:13:44+00:00" - }, - { - "name": "amphp/serialization", - "version": "v1.1.0", - "source": { - "type": "git", - "url": "https://github.com/amphp/serialization.git", - "reference": "fdf2834d78cebb0205fb2672676c1b1eb84371f0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/serialization/zipball/fdf2834d78cebb0205fb2672676c1b1eb84371f0", - "reference": "fdf2834d78cebb0205fb2672676c1b1eb84371f0", - "shasum": "" - }, - "require": { - "php": ">=7.4" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "^2", - "ext-json": "*", - "ext-zlib": "*", - "phpunit/phpunit": "^9", - "psalm/phar": "6.16.1" - }, - "type": "library", - "autoload": { - "files": [ - "src/functions.php" - ], - "psr-4": { - "Amp\\Serialization\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - } - ], - "description": "Serialization tools for IPC and data storage in PHP.", - "homepage": "https://github.com/amphp/serialization", - "keywords": [ - "async", - "asynchronous", - "serialization", - "serialize" - ], - "support": { - "issues": "https://github.com/amphp/serialization/issues", - "source": "https://github.com/amphp/serialization/tree/v1.1.0" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2026-04-05T15:59:53+00:00" - }, - { - "name": "amphp/socket", - "version": "v2.4.0", - "source": { - "type": "git", - "url": "https://github.com/amphp/socket.git", - "reference": "dadb63c5d3179fd83803e29dfeac27350e619314" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/socket/zipball/dadb63c5d3179fd83803e29dfeac27350e619314", - "reference": "dadb63c5d3179fd83803e29dfeac27350e619314", - "shasum": "" - }, - "require": { - "amphp/amp": "^3", - "amphp/byte-stream": "^2", - "amphp/dns": "^2", - "ext-openssl": "*", - "kelunik/certificate": "^1.1", - "league/uri": "^7", - "league/uri-interfaces": "^7", - "php": ">=8.1", - "revolt/event-loop": "^1" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "^2", - "amphp/phpunit-util": "^3", - "amphp/process": "^2", - "phpunit/phpunit": "^9", - "psalm/phar": "6.16.1" - }, - "type": "library", - "autoload": { - "files": [ - "src/functions.php", - "src/Internal/functions.php", - "src/SocketAddress/functions.php" - ], - "psr-4": { - "Amp\\Socket\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Lowrey", - "email": "rdlowrey@gmail.com" - }, - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - } - ], - "description": "Non-blocking socket connection / server implementations based on Amp and Revolt.", - "homepage": "https://github.com/amphp/socket", - "keywords": [ - "amp", - "async", - "encryption", - "non-blocking", - "sockets", - "tcp", - "tls" - ], - "support": { - "issues": "https://github.com/amphp/socket/issues", - "source": "https://github.com/amphp/socket/tree/v2.4.0" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2026-04-19T15:09:56+00:00" - }, - { - "name": "amphp/sync", - "version": "v2.3.0", - "source": { - "type": "git", - "url": "https://github.com/amphp/sync.git", - "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/sync/zipball/217097b785130d77cfcc58ff583cf26cd1770bf1", - "reference": "217097b785130d77cfcc58ff583cf26cd1770bf1", - "shasum": "" - }, - "require": { - "amphp/amp": "^3", - "amphp/pipeline": "^1", - "amphp/serialization": "^1", - "php": ">=8.1", - "revolt/event-loop": "^1 || ^0.2" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "^2", - "amphp/phpunit-util": "^3", - "phpunit/phpunit": "^9", - "psalm/phar": "5.23" - }, - "type": "library", - "autoload": { - "files": [ - "src/functions.php" - ], - "psr-4": { - "Amp\\Sync\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - }, - { - "name": "Stephen Coakley", - "email": "me@stephencoakley.com" - } - ], - "description": "Non-blocking synchronization primitives for PHP based on Amp and Revolt.", - "homepage": "https://github.com/amphp/sync", - "keywords": [ - "async", - "asynchronous", - "mutex", - "semaphore", - "synchronization" - ], - "support": { - "issues": "https://github.com/amphp/sync/issues", - "source": "https://github.com/amphp/sync/tree/v2.3.0" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2024-08-03T19:31:26+00:00" - }, - { - "name": "danog/advanced-json-rpc", - "version": "v3.2.3", - "source": { - "type": "git", - "url": "https://github.com/danog/php-advanced-json-rpc.git", - "reference": "ae703ea7b4811797a10590b6078de05b3b33dd91" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/danog/php-advanced-json-rpc/zipball/ae703ea7b4811797a10590b6078de05b3b33dd91", - "reference": "ae703ea7b4811797a10590b6078de05b3b33dd91", - "shasum": "" - }, - "require": { - "netresearch/jsonmapper": "^5", - "php": ">=8.1", - "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0 || ^6" - }, - "replace": { - "felixfbecker/php-advanced-json-rpc": "^3" - }, - "require-dev": { - "phpunit/phpunit": "^9" - }, - "type": "library", - "autoload": { - "psr-4": { - "AdvancedJsonRpc\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "ISC" - ], - "authors": [ - { - "name": "Felix Becker", - "email": "felix.b@outlook.com" - }, - { - "name": "Daniil Gentili", - "email": "daniil@daniil.it" - } - ], - "description": "A more advanced JSONRPC implementation", - "support": { - "issues": "https://github.com/danog/php-advanced-json-rpc/issues", - "source": "https://github.com/danog/php-advanced-json-rpc/tree/v3.2.3" - }, - "time": "2026-01-12T21:07:10+00:00" - }, - { - "name": "daverandom/libdns", - "version": "v2.1.0", - "source": { - "type": "git", - "url": "https://github.com/DaveRandom/LibDNS.git", - "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/DaveRandom/LibDNS/zipball/b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a", - "reference": "b84c94e8fe6b7ee4aecfe121bfe3b6177d303c8a", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": ">=7.1" - }, - "suggest": { - "ext-intl": "Required for IDN support" - }, - "type": "library", - "autoload": { - "files": [ - "src/functions.php" - ], - "psr-4": { - "LibDNS\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "DNS protocol implementation written in pure PHP", - "keywords": [ - "dns" - ], - "support": { - "issues": "https://github.com/DaveRandom/LibDNS/issues", - "source": "https://github.com/DaveRandom/LibDNS/tree/v2.1.0" - }, - "time": "2024-04-12T12:12:48+00:00" - }, - { - "name": "dnoegel/php-xdg-base-dir", - "version": "v0.1.1", - "source": { - "type": "git", - "url": "https://github.com/dnoegel/php-xdg-base-dir.git", - "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", - "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "require-dev": { - "phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35" - }, - "type": "library", - "autoload": { - "psr-4": { - "XdgBaseDir\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "implementation of xdg base directory specification for php", - "support": { - "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues", - "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1" - }, - "time": "2019-12-04T15:06:13+00:00" - }, - { - "name": "doctrine/data-fixtures", - "version": "1.8.2", - "source": { - "type": "git", - "url": "https://github.com/doctrine/data-fixtures.git", - "reference": "6fb221da56dae2011b33d47508e3b8aeb1d91db5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/data-fixtures/zipball/6fb221da56dae2011b33d47508e3b8aeb1d91db5", - "reference": "6fb221da56dae2011b33d47508e3b8aeb1d91db5", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^0.5.3 || ^1.0", - "doctrine/persistence": "^2.0 || ^3.0", - "php": "^7.4 || ^8.0", - "symfony/polyfill-php80": "^1" - }, - "conflict": { - "doctrine/dbal": "<3.5 || >=5", - "doctrine/orm": "<2.14 || >=4", - "doctrine/phpcr-odm": "<1.3.0" - }, - "require-dev": { - "doctrine/annotations": "^1.12 || ^2", - "doctrine/coding-standard": "^13", - "doctrine/dbal": "^3.5 || ^4", - "doctrine/mongodb-odm": "^1.3.0 || ^2.0.0", - "doctrine/orm": "^2.14 || ^3", - "ext-sqlite3": "*", - "fig/log-test": "^1", - "phpstan/phpstan": "2.1.17", - "phpunit/phpunit": "^9.6.13 || 10.5.45", - "psr/log": "^1.1 || ^2 || ^3", - "symfony/cache": "^5.4 || ^6.3 || ^7", - "symfony/var-exporter": "^5.4 || ^6.3 || ^7" - }, - "suggest": { - "alcaeus/mongo-php-adapter": "For using MongoDB ODM 1.3 with PHP 7 (deprecated)", - "doctrine/mongodb-odm": "For loading MongoDB ODM fixtures", - "doctrine/orm": "For loading ORM fixtures", - "doctrine/phpcr-odm": "For loading PHPCR ODM fixtures" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\DataFixtures\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - } - ], - "description": "Data Fixtures for all Doctrine Object Managers", - "homepage": "https://www.doctrine-project.org", - "keywords": [ - "database" - ], - "support": { - "issues": "https://github.com/doctrine/data-fixtures/issues", - "source": "https://github.com/doctrine/data-fixtures/tree/1.8.2" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdata-fixtures", - "type": "tidelift" - } - ], - "time": "2025-06-10T07:00:05+00:00" - }, - { - "name": "ergebnis/composer-normalize", - "version": "2.50.0", - "source": { - "type": "git", - "url": "https://github.com/ergebnis/composer-normalize.git", - "reference": "80971fe24ff10709789942bcbe9368b2c704097c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ergebnis/composer-normalize/zipball/80971fe24ff10709789942bcbe9368b2c704097c", - "reference": "80971fe24ff10709789942bcbe9368b2c704097c", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^2.0.0", - "ergebnis/json": "^1.4.0", - "ergebnis/json-normalizer": "^4.9.0", - "ergebnis/json-printer": "^3.7.0", - "ext-json": "*", - "justinrainbow/json-schema": "^5.2.12 || ^6.0.0", - "localheinz/diff": "^1.3.0", - "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" - }, - "require-dev": { - "composer/composer": "^2.9.4", - "ergebnis/license": "^2.7.0", - "ergebnis/php-cs-fixer-config": "^6.59.0", - "ergebnis/phpstan-rules": "^2.13.1", - "ergebnis/phpunit-slow-test-detector": "^2.20.0", - "ergebnis/rector-rules": "^1.9.0", - "fakerphp/faker": "^1.24.1", - "phpstan/extension-installer": "^1.4.3", - "phpstan/phpstan": "^2.1.38", - "phpstan/phpstan-deprecation-rules": "^2.0.3", - "phpstan/phpstan-phpunit": "^2.0.12", - "phpstan/phpstan-strict-rules": "^2.0.8", - "phpunit/phpunit": "^9.6.33", - "rector/rector": "^2.3.5", - "symfony/filesystem": "^5.4.41" - }, - "type": "composer-plugin", - "extra": { - "class": "Ergebnis\\Composer\\Normalize\\NormalizePlugin", - "branch-alias": { - "dev-main": "2.49-dev" - }, - "plugin-optional": true, - "composer-normalize": { - "indent-size": 2, - "indent-style": "space" - } - }, - "autoload": { - "psr-4": { - "Ergebnis\\Composer\\Normalize\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Andreas Möller", - "email": "am@localheinz.com", - "homepage": "https://localheinz.com" - } - ], - "description": "Provides a composer plugin for normalizing composer.json.", - "homepage": "https://github.com/ergebnis/composer-normalize", - "keywords": [ - "composer", - "normalize", - "normalizer", - "plugin" - ], - "support": { - "issues": "https://github.com/ergebnis/composer-normalize/issues", - "security": "https://github.com/ergebnis/composer-normalize/blob/main/.github/SECURITY.md", - "source": "https://github.com/ergebnis/composer-normalize" - }, - "time": "2026-02-09T20:57:47+00:00" - }, - { - "name": "ergebnis/json", - "version": "1.6.0", - "source": { - "type": "git", - "url": "https://github.com/ergebnis/json.git", - "reference": "7b56d2b5d9e897e75b43e2e753075a0904c921b1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ergebnis/json/zipball/7b56d2b5d9e897e75b43e2e753075a0904c921b1", - "reference": "7b56d2b5d9e897e75b43e2e753075a0904c921b1", - "shasum": "" - }, - "require": { - "ext-json": "*", - "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" - }, - "require-dev": { - "ergebnis/composer-normalize": "^2.44.0", - "ergebnis/data-provider": "^3.3.0", - "ergebnis/license": "^2.5.0", - "ergebnis/php-cs-fixer-config": "^6.37.0", - "ergebnis/phpstan-rules": "^2.11.0", - "ergebnis/phpunit-slow-test-detector": "^2.16.1", - "fakerphp/faker": "^1.24.0", - "infection/infection": "~0.26.6", - "phpstan/extension-installer": "^1.4.3", - "phpstan/phpstan": "^2.1.22", - "phpstan/phpstan-deprecation-rules": "^2.0.3", - "phpstan/phpstan-phpunit": "^2.0.7", - "phpstan/phpstan-strict-rules": "^2.0.6", - "phpunit/phpunit": "^9.6.24", - "rector/rector": "^2.1.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.7-dev" - }, - "composer-normalize": { - "indent-size": 2, - "indent-style": "space" - } - }, - "autoload": { - "psr-4": { - "Ergebnis\\Json\\": "src/" + "Ergebnis\\Json\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -12675,6 +11917,51 @@ }, "time": "2024-11-21T13:46:39+00:00" }, + { + "name": "felixfbecker/advanced-json-rpc", + "version": "v3.2.1", + "source": { + "type": "git", + "url": "https://github.com/felixfbecker/php-advanced-json-rpc.git", + "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/felixfbecker/php-advanced-json-rpc/zipball/b5f37dbff9a8ad360ca341f3240dc1c168b45447", + "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447", + "shasum": "" + }, + "require": { + "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", + "php": "^7.1 || ^8.0", + "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "AdvancedJsonRpc\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Felix Becker", + "email": "felix.b@outlook.com" + } + ], + "description": "A more advanced JSONRPC implementation", + "support": { + "issues": "https://github.com/felixfbecker/php-advanced-json-rpc/issues", + "source": "https://github.com/felixfbecker/php-advanced-json-rpc/tree/v3.2.1" + }, + "time": "2021-06-11T22:34:44+00:00" + }, { "name": "felixfbecker/language-server-protocol", "version": "v1.5.3", @@ -12805,246 +12092,6 @@ }, "time": "2023-12-03T23:53:29+00:00" }, - { - "name": "kelunik/certificate", - "version": "v1.1.3", - "source": { - "type": "git", - "url": "https://github.com/kelunik/certificate.git", - "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/kelunik/certificate/zipball/7e00d498c264d5eb4f78c69f41c8bd6719c0199e", - "reference": "7e00d498c264d5eb4f78c69f41c8bd6719c0199e", - "shasum": "" - }, - "require": { - "ext-openssl": "*", - "php": ">=7.0" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "^2", - "phpunit/phpunit": "^6 | 7 | ^8 | ^9" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Kelunik\\Certificate\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - } - ], - "description": "Access certificate details and transform between different formats.", - "keywords": [ - "DER", - "certificate", - "certificates", - "openssl", - "pem", - "x509" - ], - "support": { - "issues": "https://github.com/kelunik/certificate/issues", - "source": "https://github.com/kelunik/certificate/tree/v1.1.3" - }, - "time": "2023-02-03T21:26:53+00:00" - }, - { - "name": "league/uri", - "version": "7.8.1", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/uri.git", - "reference": "08cf38e3924d4f56238125547b5720496fac8fd4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri/zipball/08cf38e3924d4f56238125547b5720496fac8fd4", - "reference": "08cf38e3924d4f56238125547b5720496fac8fd4", - "shasum": "" - }, - "require": { - "league/uri-interfaces": "^7.8.1", - "php": "^8.1", - "psr/http-factory": "^1" - }, - "conflict": { - "league/uri-schemes": "^1.0" - }, - "suggest": { - "ext-bcmath": "to improve IPV4 host parsing", - "ext-dom": "to convert the URI into an HTML anchor tag", - "ext-fileinfo": "to create Data URI from file contennts", - "ext-gmp": "to improve IPV4 host parsing", - "ext-intl": "to handle IDN host with the best performance", - "ext-uri": "to use the PHP native URI class", - "jeremykendall/php-domain-parser": "to further parse the URI host and resolve its Public Suffix and Top Level Domain", - "league/uri-components": "to provide additional tools to manipulate URI objects components", - "league/uri-polyfill": "to backport the PHP URI extension for older versions of PHP", - "php-64bit": "to improve IPV4 host parsing", - "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", - "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "7.x-dev" - } - }, - "autoload": { - "psr-4": { - "League\\Uri\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ignace Nyamagana Butera", - "email": "nyamsprod@gmail.com", - "homepage": "https://nyamsprod.com" - } - ], - "description": "URI manipulation library", - "homepage": "https://uri.thephpleague.com", - "keywords": [ - "URN", - "data-uri", - "file-uri", - "ftp", - "hostname", - "http", - "https", - "middleware", - "parse_str", - "parse_url", - "psr-7", - "query-string", - "querystring", - "rfc2141", - "rfc3986", - "rfc3987", - "rfc6570", - "rfc8141", - "uri", - "uri-template", - "url", - "ws" - ], - "support": { - "docs": "https://uri.thephpleague.com", - "forum": "https://thephpleague.slack.com", - "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri/tree/7.8.1" - }, - "funding": [ - { - "url": "https://github.com/sponsors/nyamsprod", - "type": "github" - } - ], - "time": "2026-03-15T20:22:25+00:00" - }, - { - "name": "league/uri-interfaces", - "version": "7.8.1", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/uri-interfaces.git", - "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/85d5c77c5d6d3af6c54db4a78246364908f3c928", - "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928", - "shasum": "" - }, - "require": { - "ext-filter": "*", - "php": "^8.1", - "psr/http-message": "^1.1 || ^2.0" - }, - "suggest": { - "ext-bcmath": "to improve IPV4 host parsing", - "ext-gmp": "to improve IPV4 host parsing", - "ext-intl": "to handle IDN host with the best performance", - "php-64bit": "to improve IPV4 host parsing", - "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", - "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "7.x-dev" - } - }, - "autoload": { - "psr-4": { - "League\\Uri\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ignace Nyamagana Butera", - "email": "nyamsprod@gmail.com", - "homepage": "https://nyamsprod.com" - } - ], - "description": "Common tools for parsing and resolving RFC3987/RFC3986 URI", - "homepage": "https://uri.thephpleague.com", - "keywords": [ - "data-uri", - "file-uri", - "ftp", - "hostname", - "http", - "https", - "parse_str", - "parse_url", - "psr-7", - "query-string", - "querystring", - "rfc3986", - "rfc3987", - "rfc6570", - "uri", - "url", - "ws" - ], - "support": { - "docs": "https://uri.thephpleague.com", - "forum": "https://thephpleague.slack.com", - "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri-interfaces/tree/7.8.1" - }, - "funding": [ - { - "url": "https://github.com/sponsors/nyamsprod", - "type": "github" - } - ], - "time": "2026-03-08T20:05:35+00:00" - }, { "name": "localheinz/diff", "version": "1.3.0", @@ -13257,16 +12304,16 @@ }, { "name": "netresearch/jsonmapper", - "version": "v5.0.1", + "version": "v4.5.0", "source": { "type": "git", "url": "https://github.com/cweiske/jsonmapper.git", - "reference": "980674efdda65913492d29a8fd51c82270dd37bb" + "reference": "8e76efb98ee8b6afc54687045e1b8dba55ac76e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/980674efdda65913492d29a8fd51c82270dd37bb", - "reference": "980674efdda65913492d29a8fd51c82270dd37bb", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8e76efb98ee8b6afc54687045e1b8dba55ac76e5", + "reference": "8e76efb98ee8b6afc54687045e1b8dba55ac76e5", "shasum": "" }, "require": { @@ -13302,43 +12349,36 @@ "support": { "email": "cweiske@cweiske.de", "issues": "https://github.com/cweiske/jsonmapper/issues", - "source": "https://github.com/cweiske/jsonmapper/tree/v5.0.1" + "source": "https://github.com/cweiske/jsonmapper/tree/v4.5.0" }, - "time": "2026-02-22T16:28:03+00:00" + "time": "2024-09-08T10:13:13+00:00" }, { "name": "nikic/php-parser", - "version": "v5.7.0", + "version": "v4.19.5", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" + "reference": "51bd93cc741b7fc3d63d20b6bdcd99fdaa359837" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", - "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/51bd93cc741b7fc3d63d20b6bdcd99fdaa359837", + "reference": "51bd93cc741b7fc3d63d20b6bdcd99fdaa359837", "shasum": "" }, "require": { - "ext-ctype": "*", - "ext-json": "*", "ext-tokenizer": "*", - "php": ">=7.4" + "php": ">=7.1" }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^9.0" + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" }, "bin": [ "bin/php-parse" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, "autoload": { "psr-4": { "PhpParser\\": "lib/PhpParser" @@ -13360,9 +12400,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.19.5" }, - "time": "2025-12-06T11:56:16+00:00" + "time": "2025-12-06T11:45:25+00:00" }, { "name": "phar-io/manifest", @@ -13967,35 +13007,34 @@ }, { "name": "psalm/plugin-symfony", - "version": "v5.3.0", + "version": "v5.2.6", "source": { "type": "git", "url": "https://github.com/psalm/psalm-plugin-symfony.git", - "reference": "df874ff48ae3b1833ea17fba7c21b31a4d682614" + "reference": "1638f172b4ae5f283ee6b1b58fff3e44aa628742" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/psalm/psalm-plugin-symfony/zipball/df874ff48ae3b1833ea17fba7c21b31a4d682614", - "reference": "df874ff48ae3b1833ea17fba7c21b31a4d682614", + "url": "https://api.github.com/repos/psalm/psalm-plugin-symfony/zipball/1638f172b4ae5f283ee6b1b58fff3e44aa628742", + "reference": "1638f172b4ae5f283ee6b1b58fff3e44aa628742", "shasum": "" }, "require": { "ext-simplexml": "*", - "php": ">=8.1", - "symfony/expression-language": "^5.0 || ^6.0 || ^7.0 || ^8.0", - "symfony/framework-bundle": "^5.0 || ^6.0 || ^7.0 || ^8.0", - "vimeo/psalm": "^6 || dev-master" + "php": "^8.1", + "symfony/framework-bundle": "^5.0 || ^6.0 || ^7.0", + "vimeo/psalm": "^5.16 || dev-master" }, "require-dev": { "doctrine/annotations": "^1.8|^2", - "doctrine/orm": "^2.9 || ^3.6", + "doctrine/orm": "^2.9", "phpunit/phpunit": "~7.5 || ~9.5", - "symfony/cache-contracts": "^1.0 || ^2.0 || ^3.0", + "symfony/cache-contracts": "^1.0 || ^2.0", "symfony/console": "*", - "symfony/form": "^5.0 || ^6.0 || ^7.0 || ^8.0", - "symfony/messenger": "^5.0 || ^6.0 || ^7.0 || ^8.0", + "symfony/form": "^5.0 || ^6.0 || ^7.0", + "symfony/messenger": "^5.0 || ^6.0 || ^7.0", "symfony/security-core": "*", - "symfony/serializer": "^5.0 || ^6.0 || ^7.0 || ^8.0", + "symfony/serializer": "^5.0 || ^6.0 || ^7.0", "symfony/validator": "*", "twig/twig": "^2.10 || ^3.0", "weirdan/codeception-psalm-module": "dev-master" @@ -14027,9 +13066,9 @@ "description": "Psalm Plugin for Symfony", "support": { "issues": "https://github.com/psalm/psalm-plugin-symfony/issues", - "source": "https://github.com/psalm/psalm-plugin-symfony/tree/v5.3.0" + "source": "https://github.com/psalm/psalm-plugin-symfony/tree/v5.2.6" }, - "time": "2026-02-12T11:24:16+00:00" + "time": "2024-12-21T08:46:26+00:00" }, { "name": "rector/rector", @@ -14090,78 +13129,6 @@ ], "time": "2024-11-08T13:59:10+00:00" }, - { - "name": "revolt/event-loop", - "version": "v1.0.8", - "source": { - "type": "git", - "url": "https://github.com/revoltphp/event-loop.git", - "reference": "b6fc06dce8e9b523c9946138fa5e62181934f91c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/revoltphp/event-loop/zipball/b6fc06dce8e9b523c9946138fa5e62181934f91c", - "reference": "b6fc06dce8e9b523c9946138fa5e62181934f91c", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "ext-json": "*", - "jetbrains/phpstorm-stubs": "^2019.3", - "phpunit/phpunit": "^9", - "psalm/phar": "^5.15" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Revolt\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "ceesjank@gmail.com" - }, - { - "name": "Christian Lück", - "email": "christian@clue.engineering" - }, - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - } - ], - "description": "Rock-solid event loop for concurrent PHP applications.", - "keywords": [ - "async", - "asynchronous", - "concurrency", - "event", - "event-loop", - "non-blocking", - "scheduler" - ], - "support": { - "issues": "https://github.com/revoltphp/event-loop/issues", - "source": "https://github.com/revoltphp/event-loop/tree/v1.0.8" - }, - "time": "2025-08-27T21:33:23+00:00" - }, { "name": "sebastian/cli-parser", "version": "1.0.2", @@ -15814,26 +14781,24 @@ }, { "name": "vimeo/psalm", - "version": "6.16.1", + "version": "5.26.1", "source": { "type": "git", "url": "https://github.com/vimeo/psalm.git", - "reference": "f1f5de594dc76faf8784e02d3dc4716c91c6f6ac" + "reference": "d747f6500b38ac4f7dfc5edbcae6e4b637d7add0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/f1f5de594dc76faf8784e02d3dc4716c91c6f6ac", - "reference": "f1f5de594dc76faf8784e02d3dc4716c91c6f6ac", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/d747f6500b38ac4f7dfc5edbcae6e4b637d7add0", + "reference": "d747f6500b38ac4f7dfc5edbcae6e4b637d7add0", "shasum": "" }, "require": { - "amphp/amp": "^3", - "amphp/byte-stream": "^2", - "amphp/parallel": "^2.3", + "amphp/amp": "^2.4.2", + "amphp/byte-stream": "^1.5", "composer-runtime-api": "^2", "composer/semver": "^1.4 || ^2.0 || ^3.0", "composer/xdebug-handler": "^2.0 || ^3.0", - "danog/advanced-json-rpc": "^3.1", "dnoegel/php-xdg-base-dir": "^0.1.1", "ext-ctype": "*", "ext-dom": "*", @@ -15842,26 +14807,27 @@ "ext-mbstring": "*", "ext-simplexml": "*", "ext-tokenizer": "*", - "felixfbecker/language-server-protocol": "^1.5.3", + "felixfbecker/advanced-json-rpc": "^3.1", + "felixfbecker/language-server-protocol": "^1.5.2", "fidry/cpu-core-counter": "^0.4.1 || ^0.5.1 || ^1.0.0", - "netresearch/jsonmapper": "^5.0", - "nikic/php-parser": "^5.0.0", - "php": "~8.1.31 || ~8.2.27 || ~8.3.16 || ~8.4.3 || ~8.5.0", - "sebastian/diff": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0", + "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", + "nikic/php-parser": "^4.17", + "php": "^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0", + "sebastian/diff": "^4.0 || ^5.0 || ^6.0", "spatie/array-to-xml": "^2.17.0 || ^3.0", - "symfony/console": "^6.0 || ^7.0 || ^8.0", - "symfony/filesystem": "~6.3.12 || ~6.4.3 || ^7.0.3 || ^8.0", - "symfony/polyfill-php84": "^1.31.0" + "symfony/console": "^4.1.6 || ^5.0 || ^6.0 || ^7.0", + "symfony/filesystem": "^5.4 || ^6.0 || ^7.0" + }, + "conflict": { + "nikic/php-parser": "4.17.0" }, "provide": { "psalm/psalm": "self.version" }, "require-dev": { - "amphp/phpunit-util": "^3", + "amphp/phpunit-util": "^2.0", "bamarni/composer-bin-plugin": "^1.4", "brianium/paratest": "^6.9", - "danog/class-finder": "^0.4.8", - "dg/bypass-finals": "^1.5", "ext-curl": "*", "mockery/mockery": "^1.5", "nunomaduro/mock-final-classes": "^1.1", @@ -15869,10 +14835,10 @@ "phpstan/phpdoc-parser": "^1.6", "phpunit/phpunit": "^9.6", "psalm/plugin-mockery": "^1.1", - "psalm/plugin-phpunit": "^0.19", + "psalm/plugin-phpunit": "^0.18", "slevomat/coding-standard": "^8.4", "squizlabs/php_codesniffer": "^3.6", - "symfony/process": "^6.0 || ^7.0 || ^8.0" + "symfony/process": "^4.4 || ^5.0 || ^6.0 || ^7.0" }, "suggest": { "ext-curl": "In order to send data to shepherd", @@ -15883,7 +14849,6 @@ "psalm-language-server", "psalm-plugin", "psalm-refactor", - "psalm-review", "psalter" ], "type": "project", @@ -15893,9 +14858,7 @@ "dev-2.x": "2.x-dev", "dev-3.x": "3.x-dev", "dev-4.x": "4.x-dev", - "dev-5.x": "5.x-dev", - "dev-6.x": "6.x-dev", - "dev-master": "7.x-dev" + "dev-master": "5.x-dev" } }, "autoload": { @@ -15910,10 +14873,6 @@ "authors": [ { "name": "Matthew Brown" - }, - { - "name": "Daniil Gentili", - "email": "daniil@daniil.it" } ], "description": "A static analysis tool for finding errors in PHP applications", @@ -15928,7 +14887,7 @@ "issues": "https://github.com/vimeo/psalm/issues", "source": "https://github.com/vimeo/psalm" }, - "time": "2026-03-19T10:56:09+00:00" + "time": "2024-09-08T18:53:08+00:00" }, { "name": "vincentlanglet/twig-cs-fixer", @@ -16009,22 +14968,22 @@ }, { "name": "weirdan/doctrine-psalm-plugin", - "version": "v2.10.0", + "version": "v2.9.0", "source": { "type": "git", "url": "https://github.com/psalm/psalm-plugin-doctrine.git", - "reference": "8d604c817976e156cd6f1cfab983eadd35b04a2f" + "reference": "3db8e55b2ea15373338d2a3eab71c5f5a31c8b08" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/psalm/psalm-plugin-doctrine/zipball/8d604c817976e156cd6f1cfab983eadd35b04a2f", - "reference": "8d604c817976e156cd6f1cfab983eadd35b04a2f", + "url": "https://api.github.com/repos/psalm/psalm-plugin-doctrine/zipball/3db8e55b2ea15373338d2a3eab71c5f5a31c8b08", + "reference": "3db8e55b2ea15373338d2a3eab71c5f5a31c8b08", "shasum": "" }, "require": { "composer/semver": "^1.4 || ^2.0 || ^3.0", - "php": "^8", - "vimeo/psalm": "^6" + "php": "^7.2 || ^8", + "vimeo/psalm": "^4.28|^5.0" }, "conflict": { "doctrine/collections": "<1.8", @@ -16081,9 +15040,9 @@ ], "support": { "issues": "https://github.com/psalm/psalm-plugin-doctrine/issues", - "source": "https://github.com/psalm/psalm-plugin-doctrine/tree/v2.10.0" + "source": "https://github.com/psalm/psalm-plugin-doctrine/tree/v2.9.0" }, - "time": "2025-01-26T11:36:27+00:00" + "time": "2023-07-15T05:44:30+00:00" } ], "aliases": [], diff --git a/docker-compose.server.yml b/docker-compose.server.yml index bc011cac0..614d67b10 100644 --- a/docker-compose.server.yml +++ b/docker-compose.server.yml @@ -8,7 +8,7 @@ networks: services: phpfpm: - image: itkdev/php8.4-fpm:alpine + image: itkdev/php8.3-fpm:alpine restart: unless-stopped networks: - app diff --git a/docker-compose.yml b/docker-compose.yml index b18ab3814..b5c08946d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -27,7 +27,7 @@ services: #- ENCRYPT=1 # Uncomment to enable database encryption. phpfpm: - image: itkdev/php8.4-fpm:latest + image: itkdev/php8.3-fpm:latest user: ${COMPOSE_USER:-deploy} networks: - app diff --git a/symfony.lock b/symfony.lock index ec68755be..280d87147 100644 --- a/symfony.lock +++ b/symfony.lock @@ -123,6 +123,9 @@ "fakerphp/faker": { "version": "v1.15.0" }, + "felixfbecker/advanced-json-rpc": { + "version": "v3.2.1" + }, "felixfbecker/language-server-protocol": { "version": "1.5.1" }, From 4f4ca2ba5bc9fcb9afa164767688d3daa6d09ba8 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Tue, 28 Apr 2026 11:20:32 +0200 Subject: [PATCH 13/59] 370: Fix nginx static-file location to fall back to PHP The regex location for static files (jpg, png, etc.) had no try_files directive, so first-time requests to /media/cache/resolve/... 404'd without ever reaching Symfony. Adding the same fallback as location / lets LiipImagineBundle generate missing thumbnails on demand. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 1 + infrastructure/nginx/etc/templates/default.conf.template | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7040a316..2ac94d66d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ All notable changes to this project will be documented in this file. - Removed fixture length check from test. - Added vitest for frontend unit tests. - Updated infrastructure and image build for mono-repo. +- Fixed nginx static-file location to fall back to PHP so LiipImagineBundle can generate missing thumbnails (#370). ### NB! Prior to 3.x the project was split into separate repositories diff --git a/infrastructure/nginx/etc/templates/default.conf.template b/infrastructure/nginx/etc/templates/default.conf.template index 52269bea8..7902373a6 100644 --- a/infrastructure/nginx/etc/templates/default.conf.template +++ b/infrastructure/nginx/etc/templates/default.conf.template @@ -59,6 +59,7 @@ server { expires 1y; add_header Cache-Control "public, immutable"; access_log off; + try_files $uri /index.php$is_args$args; } # Screen client online check should just serve static files From fcfe0de9963f5045f757d34d89380395286490bf Mon Sep 17 00:00:00 2001 From: turegjorup Date: Tue, 28 Apr 2026 11:31:37 +0200 Subject: [PATCH 14/59] 370: Unify nginx config so dev mirrors prod Local dev had its own minimal default.conf.template under .docker/, which lacked the production-only directives (regex static-files location, rate limiting, /health, security headers). That gap is exactly what hid the LiipImagineBundle thumbnail 404 from local testing. Move all dev compose files to mount the same templates/nginx.conf shipped in the production nginx image, parameterize the web root via NGINX_WEB_ROOT (prod default set in the Dockerfile), and delete the redundant .docker/ copies. Co-Authored-By: Claude Opus 4.7 (1M context) --- .docker/nginx.conf | 36 ------------- .docker/templates/default.conf.template | 52 ------------------- CHANGELOG.md | 1 + docker-compose.server.yml | 5 +- docker-compose.yml | 4 +- infrastructure/nginx/Dockerfile | 3 +- .../nginx/etc/templates/default.conf.template | 2 +- 7 files changed, 10 insertions(+), 93 deletions(-) delete mode 100644 .docker/nginx.conf delete mode 100644 .docker/templates/default.conf.template diff --git a/.docker/nginx.conf b/.docker/nginx.conf deleted file mode 100644 index 8fe03dbc7..000000000 --- a/.docker/nginx.conf +++ /dev/null @@ -1,36 +0,0 @@ -worker_processes auto; - -error_log /dev/stderr notice; -pid /tmp/nginx.pid; - -events { - worker_connections 1024; -} - -http { - proxy_temp_path /tmp/proxy_temp; - client_body_temp_path /tmp/client_temp; - fastcgi_temp_path /tmp/fastcgi_temp; - uwsgi_temp_path /tmp/uwsgi_temp; - scgi_temp_path /tmp/scgi_temp; - - include /etc/nginx/mime.types; - default_type application/octet-stream; - - set_real_ip_from 172.16.0.0/16; - real_ip_recursive on; - real_ip_header X-Forwarded-For; - - log_format main '$http_x_real_ip - $remote_user [$time_local] "$request" ' - '$status $body_bytes_sent "$http_referer" ' - '"$http_user_agent" "$http_x_forwarded_for"'; - - access_log /dev/stdout main; - - sendfile on; - keepalive_timeout 65; - - gzip on; - - include /etc/nginx/conf.d/*.conf; -} diff --git a/.docker/templates/default.conf.template b/.docker/templates/default.conf.template deleted file mode 100644 index 8f789dca5..000000000 --- a/.docker/templates/default.conf.template +++ /dev/null @@ -1,52 +0,0 @@ -server { - listen ${NGINX_PORT}; - server_name localhost; - - root ${NGINX_WEB_ROOT}; - - client_max_body_size ${NGINX_MAX_BODY_SIZE}; - - # This also needs to be set in the single server tag and not only in http. - set_real_ip_from 172.16.0.0/16; - real_ip_recursive on; - real_ip_header X-Forwarded-For; - - location / { - # try to serve file directly, fallback to index.php - try_files $uri /index.php$is_args$args; - } - - # Screen client online check should just serve static files - location /client/online-check { - index index.html index.htm; - } - - # Protect files and directories from prying eyes. - location ~* \.(engine|inc|install|make|module|profile|po|sh|.*sql|.tar|.gz|.bz2|theme|twig|tpl(\.php)?|xtmpl|yml)(~|\.sw[op]|\.bak|\.orig|\.save)?$|^(\.(?!well-known).*|Entries.*|Repository|Root|Tag|Template|composer\.(json|lock)|web\.config)$|^#.*#$|\.php(~|\.sw[op]|\.bak|\.orig|\.save)$ { - deny all; - return 404; - } - - location ~ ^/index\.php(/|$) { - fastcgi_buffers 16 32k; - fastcgi_buffer_size 64k; - fastcgi_busy_buffers_size 64k; - - fastcgi_pass ${NGINX_FPM_SERVICE}; - fastcgi_split_path_info ^(.+\.php)(/.*)$; - include fastcgi_params; - - fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; - fastcgi_param DOCUMENT_ROOT $realpath_root; - - internal; - } - - location ~ \.php$ { - return 404; - } - - # Send log message to files symlinked to stdout/stderr. - error_log /dev/stderr; - access_log /dev/stdout main; -} diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ac94d66d..a68587870 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ All notable changes to this project will be documented in this file. - Added vitest for frontend unit tests. - Updated infrastructure and image build for mono-repo. - Fixed nginx static-file location to fall back to PHP so LiipImagineBundle can generate missing thumbnails (#370). +- Unified nginx config: dev compose now mounts the production template/nginx.conf so local matches deployed behavior (#370). ### NB! Prior to 3.x the project was split into separate repositories diff --git a/docker-compose.server.yml b/docker-compose.server.yml index 614d67b10..d9a04931f 100644 --- a/docker-compose.server.yml +++ b/docker-compose.server.yml @@ -29,14 +29,15 @@ services: depends_on: - phpfpm volumes: - - ./.docker/templates:/etc/nginx/templates:ro - - ./.docker/nginx.conf:/etc/nginx/nginx.conf:ro + - ./infrastructure/nginx/etc/templates:/etc/nginx/templates:ro + - ./infrastructure/nginx/etc/nginx.conf:/etc/nginx/nginx.conf:ro - .:/app environment: NGINX_FPM_SERVICE: ${COMPOSE_PROJECT_NAME}-phpfpm-1:9000 NGINX_WEB_ROOT: /app/public NGINX_PORT: 8080 NGINX_MAX_BODY_SIZE: 5M + NGINX_SET_REAL_IP_FROM: 172.16.0.0/12 labels: - "traefik.enable=true" - "traefik.docker.network=frontend" diff --git a/docker-compose.yml b/docker-compose.yml index b5c08946d..d2b8da507 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -57,13 +57,15 @@ services: ports: - "8080" volumes: - - ./.docker/templates:/etc/nginx/templates:ro + - ./infrastructure/nginx/etc/templates:/etc/nginx/templates:ro + - ./infrastructure/nginx/etc/nginx.conf:/etc/nginx/nginx.conf:ro - .:/app environment: NGINX_FPM_SERVICE: ${COMPOSE_PROJECT_NAME}-phpfpm-1:9000 NGINX_WEB_ROOT: /app/public NGINX_PORT: 8080 NGINX_MAX_BODY_SIZE: 5M + NGINX_SET_REAL_IP_FROM: 172.16.0.0/12 labels: - "traefik.enable=true" - "traefik.docker.network=frontend" diff --git a/infrastructure/nginx/Dockerfile b/infrastructure/nginx/Dockerfile index 5bf5c00f7..934e95839 100644 --- a/infrastructure/nginx/Dockerfile +++ b/infrastructure/nginx/Dockerfile @@ -72,7 +72,8 @@ ENV APP_CLIENT_PATH=/app \ APP_API_PATH=/var/www/html \ NGINX_PORT=8080 \ NGINX_SET_REAL_IP_FROM=172.17.0.0/16 \ - NGINX_MAX_BODY_SIZE=10m + NGINX_MAX_BODY_SIZE=10m \ + NGINX_WEB_ROOT=/var/www/html/public WORKDIR ${APP_API_PATH} diff --git a/infrastructure/nginx/etc/templates/default.conf.template b/infrastructure/nginx/etc/templates/default.conf.template index 7902373a6..ebb38d514 100644 --- a/infrastructure/nginx/etc/templates/default.conf.template +++ b/infrastructure/nginx/etc/templates/default.conf.template @@ -8,7 +8,7 @@ server { listen [::]:${NGINX_PORT}; server_name localhost; - root /var/www/html/public; + root ${NGINX_WEB_ROOT}; index index.php index.html; # Health check endpoint From 241b958829f196dea496f413076332b4848fa8b7 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Tue, 28 Apr 2026 13:23:49 +0200 Subject: [PATCH 15/59] 5402: Bump GitHub Actions to current major versions Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/apispec.yaml | 2 +- .github/workflows/changelog.yaml | 2 +- .github/workflows/composer.yaml | 4 ++-- .github/workflows/composer_install.yaml | 2 +- .github/workflows/doctrine.yaml | 2 +- .github/workflows/github_build_release.yml | 4 ++-- .github/workflows/javascript.yaml | 2 +- .github/workflows/markdown.yaml | 2 +- .github/workflows/php.yaml | 2 +- .github/workflows/phpunit.yaml | 2 +- .github/workflows/playwright.yaml | 4 ++-- .github/workflows/psalm.yaml | 2 +- .github/workflows/rector.yaml | 2 +- .github/workflows/styles.yaml | 2 +- .github/workflows/twig.yaml | 2 +- .github/workflows/vitest.yaml | 2 +- .github/workflows/yaml.yaml | 2 +- 17 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.github/workflows/apispec.yaml b/.github/workflows/apispec.yaml index cf3c3eac7..152f5b23c 100644 --- a/.github/workflows/apispec.yaml +++ b/.github/workflows/apispec.yaml @@ -11,7 +11,7 @@ jobs: name: API Specification validation steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 2 diff --git a/.github/workflows/changelog.yaml b/.github/workflows/changelog.yaml index 483da6e95..63638c2d1 100644 --- a/.github/workflows/changelog.yaml +++ b/.github/workflows/changelog.yaml @@ -18,7 +18,7 @@ jobs: fail-fast: false steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 2 diff --git a/.github/workflows/composer.yaml b/.github/workflows/composer.yaml index b068c235a..8787034df 100644 --- a/.github/workflows/composer.yaml +++ b/.github/workflows/composer.yaml @@ -41,7 +41,7 @@ jobs: strategy: fail-fast: false steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - run: | docker network create frontend docker compose run --rm phpfpm composer validate --strict @@ -51,7 +51,7 @@ jobs: strategy: fail-fast: false steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - run: | docker network create frontend docker compose run --rm phpfpm composer install diff --git a/.github/workflows/composer_install.yaml b/.github/workflows/composer_install.yaml index cc69d8b13..bdce83d6f 100644 --- a/.github/workflows/composer_install.yaml +++ b/.github/workflows/composer_install.yaml @@ -11,7 +11,7 @@ jobs: name: Composer install in prod mode steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup network run: docker network create frontend diff --git a/.github/workflows/doctrine.yaml b/.github/workflows/doctrine.yaml index c24edb83e..dccba03b8 100644 --- a/.github/workflows/doctrine.yaml +++ b/.github/workflows/doctrine.yaml @@ -11,7 +11,7 @@ jobs: name: Validate Schema steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup network run: docker network create frontend diff --git a/.github/workflows/github_build_release.yml b/.github/workflows/github_build_release.yml index 405dd2593..482cbdc23 100644 --- a/.github/workflows/github_build_release.yml +++ b/.github/workflows/github_build_release.yml @@ -16,10 +16,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Install Task task runner - uses: go-task/setup-task@v1 + uses: go-task/setup-task@v2 - name: Install, Build, Cleanup run: | diff --git a/.github/workflows/javascript.yaml b/.github/workflows/javascript.yaml index 801ddbe93..4dacffe6e 100644 --- a/.github/workflows/javascript.yaml +++ b/.github/workflows/javascript.yaml @@ -27,7 +27,7 @@ jobs: fail-fast: false steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - run: | docker network create frontend diff --git a/.github/workflows/markdown.yaml b/.github/workflows/markdown.yaml index 60fc0ee5c..066063a5c 100644 --- a/.github/workflows/markdown.yaml +++ b/.github/workflows/markdown.yaml @@ -34,7 +34,7 @@ jobs: fail-fast: false steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - run: | docker network create frontend diff --git a/.github/workflows/php.yaml b/.github/workflows/php.yaml index ee9679776..02dc8c532 100644 --- a/.github/workflows/php.yaml +++ b/.github/workflows/php.yaml @@ -48,7 +48,7 @@ jobs: name: PHP - Check Coding Standards runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - run: | docker network create frontend docker compose run --rm phpfpm composer install diff --git a/.github/workflows/phpunit.yaml b/.github/workflows/phpunit.yaml index a79440dd9..9daee407b 100644 --- a/.github/workflows/phpunit.yaml +++ b/.github/workflows/phpunit.yaml @@ -11,7 +11,7 @@ jobs: name: PHP Unit tests steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup network run: docker network create frontend diff --git a/.github/workflows/playwright.yaml b/.github/workflows/playwright.yaml index 5b78012d6..93ae20714 100644 --- a/.github/workflows/playwright.yaml +++ b/.github/workflows/playwright.yaml @@ -13,7 +13,7 @@ jobs: fail-fast: true steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup network run: docker network create frontend @@ -38,7 +38,7 @@ jobs: docker compose run --rm playwright npx playwright install --with-deps docker compose run --rm playwright npx playwright test - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 if: always() with: name: playwright-report diff --git a/.github/workflows/psalm.yaml b/.github/workflows/psalm.yaml index 65785a415..4179eae48 100644 --- a/.github/workflows/psalm.yaml +++ b/.github/workflows/psalm.yaml @@ -11,7 +11,7 @@ jobs: name: Psalm steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup network run: docker network create frontend diff --git a/.github/workflows/rector.yaml b/.github/workflows/rector.yaml index 68ab15301..47bacbc71 100644 --- a/.github/workflows/rector.yaml +++ b/.github/workflows/rector.yaml @@ -11,7 +11,7 @@ jobs: name: Rector steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup network run: docker network create frontend diff --git a/.github/workflows/styles.yaml b/.github/workflows/styles.yaml index edc796020..02f9d6bcd 100644 --- a/.github/workflows/styles.yaml +++ b/.github/workflows/styles.yaml @@ -27,7 +27,7 @@ jobs: fail-fast: false steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - run: | docker network create frontend diff --git a/.github/workflows/twig.yaml b/.github/workflows/twig.yaml index 9dc424b45..76e2a57d6 100644 --- a/.github/workflows/twig.yaml +++ b/.github/workflows/twig.yaml @@ -40,7 +40,7 @@ jobs: fail-fast: false steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - run: | docker network create frontend diff --git a/.github/workflows/vitest.yaml b/.github/workflows/vitest.yaml index 7203fe02f..bda6ac97f 100644 --- a/.github/workflows/vitest.yaml +++ b/.github/workflows/vitest.yaml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup network run: docker network create frontend diff --git a/.github/workflows/yaml.yaml b/.github/workflows/yaml.yaml index 1c0ada3f7..16c44b9ff 100644 --- a/.github/workflows/yaml.yaml +++ b/.github/workflows/yaml.yaml @@ -31,7 +31,7 @@ jobs: yaml-lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - run: | docker network create frontend From a19bc4bbee407fa1abd40243fdc7b78534ee90fc Mon Sep 17 00:00:00 2001 From: turegjorup Date: Tue, 28 Apr 2026 13:24:00 +0200 Subject: [PATCH 16/59] 5402: Switch image build pipeline to GHCR Consolidate the develop and tag DockerHub workflows into a single multi-arch build that pushes to ghcr.io/os2display/*, authenticated via GITHUB_TOKEN. Provide repository-root as a named build context so the Dockerfiles' COPY --from=repository-root steps resolve. Add per-image GHA cache (mode=max) so multi-arch builds reuse builder stages across runs instead of re-running npm ci + composer install per platform. Align infrastructure/build-n-push.sh with the workflow: drop the turegjorup namespace, single-pass docker buildx --push for multi-arch manifests, and add a BUILD_LOAD=1 flag for local builds that load into the host docker daemon instead of pushing. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/build-images.yml | 106 ++++++++++++++++++ .../docker_build_images_from_develop.yml | 69 ------------ .../docker_build_images_from_tag.yml | 68 ----------- CHANGELOG.md | 1 + infrastructure/build-n-push.sh | 60 ++++++---- 5 files changed, 146 insertions(+), 158 deletions(-) create mode 100644 .github/workflows/build-images.yml delete mode 100644 .github/workflows/docker_build_images_from_develop.yml delete mode 100644 .github/workflows/docker_build_images_from_tag.yml diff --git a/.github/workflows/build-images.yml b/.github/workflows/build-images.yml new file mode 100644 index 000000000..d0a51098d --- /dev/null +++ b/.github/workflows/build-images.yml @@ -0,0 +1,106 @@ +--- +name: Build docker images + +on: + push: + branches: + - develop + - "release/*" + tags: + - "*" + +jobs: + docker: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v6 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Login to GHCR + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Docker meta (API) + id: meta-api + uses: docker/metadata-action@v6 + with: + images: ghcr.io/os2display/display-api-service + tags: | + # develop branch → 'develop' (skipped on release/* pushes) + type=ref,event=branch,enable=${{ !startsWith(github.ref, 'refs/heads/release/') }} + # release/X.Y.Z branch → 'X.Y.Z-rc' + type=match,pattern=release/(\d+\.\d+\.\d+),group=1,suffix=-rc + # version tags → 'X.Y.Z' + type=ref,event=tag + type=semver,pattern={{version}} + + - name: Build and push (API) + uses: docker/build-push-action@v7 + with: + context: ./infrastructure/display-api-service/ + file: ./infrastructure/display-api-service/Dockerfile + build-contexts: | + repository-root=. + # Use the version computed by docker/metadata-action so APP_VERSION + # matches the image tag for every trigger ('develop' / '3.0.0' / + # '3.0.0-rc'), instead of github.ref_name which would be 'release/3.0.0'. + build-args: | + APP_VERSION=${{ steps.meta-api.outputs.version }} + APP_RELEASE_VERSION=${{ steps.meta-api.outputs.version }} + APP_RELEASE_TIMESTAMP=${{ github.run_number }} + push: true + tags: ${{ steps.meta-api.outputs.tags }} + labels: ${{ steps.meta-api.outputs.labels }} + provenance: mode=max + sbom: true + platforms: linux/amd64,linux/arm64 + # mode=max caches builder stages (client_app_builder, api_app_builder), + # which is what makes multi-arch builds practical to cache at all. + # Scoped per image so API and Nginx caches don't evict each other. + cache-from: type=gha,scope=display-api-service + cache-to: type=gha,mode=max,scope=display-api-service + + - name: Docker meta (Nginx) + id: meta-nginx + uses: docker/metadata-action@v6 + with: + images: ghcr.io/os2display/display-api-service-nginx + tags: | + # develop branch → 'develop' (skipped on release/* pushes) + type=ref,event=branch,enable=${{ !startsWith(github.ref, 'refs/heads/release/') }} + # release/X.Y.Z branch → 'X.Y.Z-rc' + type=match,pattern=release/(\d+\.\d+\.\d+),group=1,suffix=-rc + # version tags → 'X.Y.Z' + type=ref,event=tag + type=semver,pattern={{version}} + + - name: Build and push (Nginx) + uses: docker/build-push-action@v7 + with: + context: ./infrastructure/nginx/ + file: ./infrastructure/nginx/Dockerfile + build-contexts: | + repository-root=. + build-args: | + APP_VERSION=${{ steps.meta-nginx.outputs.version }} + APP_RELEASE_VERSION=${{ steps.meta-nginx.outputs.version }} + APP_RELEASE_TIMESTAMP=${{ github.run_number }} + push: true + tags: ${{ steps.meta-nginx.outputs.tags }} + labels: ${{ steps.meta-nginx.outputs.labels }} + provenance: mode=max + sbom: true + platforms: linux/amd64,linux/arm64 + cache-from: type=gha,scope=display-api-service-nginx + cache-to: type=gha,mode=max,scope=display-api-service-nginx diff --git a/.github/workflows/docker_build_images_from_develop.yml b/.github/workflows/docker_build_images_from_develop.yml deleted file mode 100644 index 0becba3b0..000000000 --- a/.github/workflows/docker_build_images_from_develop.yml +++ /dev/null @@ -1,69 +0,0 @@ ---- -on: - push: - branches: - - "develop" - -# This Action builds to os2display/* using ./infrastructure/* -name: Build docker image (develop) - -jobs: - docker: - runs-on: ubuntu-latest - env: - APP_VERSION: develop - COMPOSER_ALLOW_SUPERUSER: 1 - steps: - - name: Login to DockerHub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USER }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - # Build api - - name: Docker meta (API) - id: meta-api - uses: docker/metadata-action@v5 - with: - images: os2display/display-api-service - - - name: Build and push (API) - uses: docker/build-push-action@v6 - with: - context: ./infrastructure/display-api-service/ - file: ./infrastructure/display-api-service/Dockerfile - build-args: | - VERSION=${{ env.APP_VERSION }} - push: true - tags: ${{ steps.meta-api.outputs.tags }} - labels: ${{ steps.meta-api.outputs.labels }} - provenance: mode=max - sbom: true - platforms: linux/amd64,linux/arm64 - - # Build nginx (depends on api build) - - name: Docker meta (Nginx) - id: meta-nginx - uses: docker/metadata-action@v5 - with: - images: os2display/display-api-service-nginx - - - name: Build and push (Nginx) - uses: docker/build-push-action@v6 - with: - context: ./infrastructure/nginx/ - file: ./infrastructure/nginx/Dockerfile - build-args: | - APP_VERSION=${{ env.APP_VERSION }} - push: true - tags: ${{ steps.meta-nginx.outputs.tags }} - labels: ${{ steps.meta-nginx.outputs.labels }} - provenance: mode=max - sbom: true - platforms: linux/amd64,linux/arm64 diff --git a/.github/workflows/docker_build_images_from_tag.yml b/.github/workflows/docker_build_images_from_tag.yml deleted file mode 100644 index 4b441be89..000000000 --- a/.github/workflows/docker_build_images_from_tag.yml +++ /dev/null @@ -1,68 +0,0 @@ ---- -on: - push: - tags: - - "*" - -# This Action builds to os2display/* using ./infrastructure/* -name: Build docker image (tag) - -jobs: - docker: - runs-on: ubuntu-latest - steps: - - name: Login to DockerHub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USER }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - # Build api - - name: Docker meta (API) - id: meta-api - uses: docker/metadata-action@v5 - with: - images: os2display/display-api-service - - - name: Build and push (API) - uses: docker/build-push-action@v6 - with: - context: ./infrastructure/display-api-service/ - file: ./infrastructure/display-api-service/Dockerfile - build-args: | - APP_VERSION=${{ github.ref }} - push: true - tags: ${{ steps.meta-api.outputs.tags }} - labels: ${{ steps.meta-api.outputs.labels }} - provenance: mode=max - sbom: true - - # Build nginx (depends on api build) - - name: Docker meta (Nginx) - id: meta-nginx - uses: docker/metadata-action@v5 - with: - images: os2display/display-api-service-nginx - - - name: Get the tag - id: get_tag - run: echo ::set-output name=git_tag::$(echo $GITHUB_REF_NAME) - - - name: Build and push (Nginx) - uses: docker/build-push-action@v5 - with: - context: ./infrastructure/nginx/ - file: ./infrastructure/nginx/Dockerfile - build-args: | - APP_VERSION=${{ steps.get_tag.outputs.git_tag }} - push: true - tags: ${{ steps.meta-nginx.outputs.tags }} - labels: ${{ steps.meta-nginx.outputs.labels }} - provenance: mode=max - sbom: true diff --git a/CHANGELOG.md b/CHANGELOG.md index d7040a316..f6cf5c243 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ All notable changes to this project will be documented in this file. - Removed fixture length check from test. - Added vitest for frontend unit tests. - Updated infrastructure and image build for mono-repo. +- Switched image build pipeline to GHCR with multi-arch layer caching. ### NB! Prior to 3.x the project was split into separate repositories diff --git a/infrastructure/build-n-push.sh b/infrastructure/build-n-push.sh index 04f244043..0f64eee74 100755 --- a/infrastructure/build-n-push.sh +++ b/infrastructure/build-n-push.sh @@ -1,27 +1,45 @@ #!/bin/sh - set -eux -APP_VERSION=develop - -docker pull itkdev/php8.4-fpm:alpine -docker pull nginxinc/nginx-unprivileged:alpine - -docker build --build-context repository-root=.. \ - --platform linux/amd64,linux/arm64 \ - --pull \ - --no-cache \ - --build-arg APP_VERSION=${APP_VERSION} \ - --tag=turegjorup/display-api-service:${APP_VERSION} \ - --file="display-api-service/Dockerfile" display-api-service +APP_VERSION="${APP_VERSION:-develop}" +REGISTRY="${REGISTRY:-ghcr.io/os2display}" +RELEASE_TIMESTAMP="$(date +%s)" +# Set BUILD_LOAD=1 to build for the host platform and load into the local +# docker daemon instead of pushing. Useful for local smoke tests; production +# builds (multi-arch + registry push) require BUILD_LOAD unset or 0. +if [ "${BUILD_LOAD:-0}" = "1" ]; then + PLATFORMS="" + OUTPUT="--load" +else + PLATFORMS="--platform=linux/amd64,linux/arm64" + OUTPUT="--push" +fi -docker build --build-context repository-root=.. \ - --platform linux/amd64,linux/arm64 \ - --no-cache \ - --build-arg VERSION=${APP_VERSION} \ - --tag=turegjorup/display-api-service-nginx:${APP_VERSION} \ - --file="nginx/Dockerfile" nginx +# API (php-fpm) image. Context is the API infra dir so the Dockerfile picks +# up docker-entrypoint.sh; repository-root is provided as a named build context +# for the COPY --from=repository-root steps. +# shellcheck disable=SC2086 +docker buildx build \ + ${PLATFORMS} \ + --pull ${OUTPUT} \ + --build-context repository-root=. \ + --build-arg APP_VERSION="${APP_VERSION}" \ + --build-arg APP_RELEASE_VERSION="${APP_VERSION}" \ + --build-arg APP_RELEASE_TIMESTAMP="${RELEASE_TIMESTAMP}" \ + --tag "${REGISTRY}/display-api-service:${APP_VERSION}" \ + --file infrastructure/display-api-service/Dockerfile \ + infrastructure/display-api-service -docker push os2display/display-api-service:${APP_VERSION} -docker push os2display/display-api-service-nginx:${APP_VERSION} +# Nginx image. Context is the nginx infra dir so 'COPY etc /etc/nginx' resolves. +# shellcheck disable=SC2086 +docker buildx build \ + ${PLATFORMS} \ + --pull ${OUTPUT} \ + --build-context repository-root=. \ + --build-arg APP_VERSION="${APP_VERSION}" \ + --build-arg APP_RELEASE_VERSION="${APP_VERSION}" \ + --build-arg APP_RELEASE_TIMESTAMP="${RELEASE_TIMESTAMP}" \ + --tag "${REGISTRY}/display-api-service-nginx:${APP_VERSION}" \ + --file infrastructure/nginx/Dockerfile \ + infrastructure/nginx From ab052aef9f999cb9dc80dadfcf37576f3226591e Mon Sep 17 00:00:00 2001 From: turegjorup Date: Tue, 28 Apr 2026 13:24:11 +0200 Subject: [PATCH 17/59] 5402: Fold chmod into COPY to drop duplicate image layers The nginx Dockerfile had: COPY --from=api_app_builder ... ./public RUN chmod -R g+w /var/www/html The RUN re-wrote every file's permissions, producing a second 19 MB layer with byte-identical content. Replacing with --chmod on the COPY itself drops the duplicate layer; image goes from 135 MB to 108 MB, dive efficiency from 78.7% to 99.5%. Apply the same pattern to docker-entrypoint.sh in the API Dockerfile for consistency. Co-Authored-By: Claude Opus 4.7 (1M context) --- infrastructure/display-api-service/Dockerfile | 3 +-- infrastructure/nginx/Dockerfile | 11 ++++------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/infrastructure/display-api-service/Dockerfile b/infrastructure/display-api-service/Dockerfile index 748268a43..be3e5b4b3 100644 --- a/infrastructure/display-api-service/Dockerfile +++ b/infrastructure/display-api-service/Dockerfile @@ -89,8 +89,7 @@ COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer # Download Prometheus php-fpm export. COPY --from=hipages/php-fpm_exporter:2.2.0 /php-fpm_exporter /usr/local/bin/php-fpm_exporter -COPY docker-entrypoint.sh /usr/local/bin/ -RUN chmod +x /usr/local/bin/docker-entrypoint.sh +COPY --chmod=0755 docker-entrypoint.sh /usr/local/bin/ USER deploy diff --git a/infrastructure/nginx/Dockerfile b/infrastructure/nginx/Dockerfile index 5bf5c00f7..eb3cb3a17 100644 --- a/infrastructure/nginx/Dockerfile +++ b/infrastructure/nginx/Dockerfile @@ -76,16 +76,13 @@ ENV APP_CLIENT_PATH=/app \ WORKDIR ${APP_API_PATH} -# Create directory and copy built client assets -RUN mkdir -p ${APP_API_PATH}/public -COPY --from=api_app_builder --chown=$UID:0 ${APP_API_PATH}/public ./public +# Copy built client assets with permissions baked in (avoids a duplicate +# layer that a separate `RUN chmod -R` would produce — saves ~19 MB). +COPY --from=api_app_builder --chown=$UID:0 --chmod=775 ${APP_API_PATH}/public ./public -# Copy configuration and entrypoint script +# Copy nginx configuration COPY --chown=$UID:0 etc /etc/nginx -# Set proper permissions (files are already owned by $UID:0 from COPY --chown) -RUN chmod -R g+w ${APP_API_PATH} - # Add health check HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost:${NGINX_PORT}/health || exit 1 From 6c3d0c1f46bbc1e487f02ae50ff571a922c928bd Mon Sep 17 00:00:00 2001 From: turegjorup Date: Tue, 28 Apr 2026 13:24:19 +0200 Subject: [PATCH 18/59] 5402: Expand .dockerignore for build-cache stability The API Dockerfile does a wholesale 'COPY --from=repository-root ./' that brings in everything not excluded. Without these additions, editing a docker-compose.yml, Taskfile.yml, or any quality-tool config (phpstan, psalm, rector, playwright) invalidated the cache from that COPY onward. Co-Authored-By: Claude Opus 4.7 (1M context) --- .dockerignore | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/.dockerignore b/.dockerignore index fc10ce7dc..7cd4dd2be 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,7 +2,7 @@ !/.git/config # Unneeded application files -/.docker/data/README.md +/.docker /.github /docs /fixtures @@ -15,6 +15,22 @@ /scripts /tests +# IDE / editor state +/.idea +/.vscode + +# Compose / task runner / dev tooling configs (not consumed at runtime) +/docker-compose*.yml +/Taskfile.yml +/playwright.config.ts + +# CI / quality-tool configs (not consumed at runtime) +/phpstan.dist.neon +/phpunit.xml.dist +/psalm.xml +/psalm-baseline.xml +/rector.php + ###> symfony/framework-bundle ### /.env.local /.env.local.php From 4e1daedac21e3a157d98a256f1e0cbff87733770 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Tue, 28 Apr 2026 13:51:16 +0200 Subject: [PATCH 19/59] 5402: Document the operator-facing configuration surface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The image carries three distinct config surfaces: - .env — Symfony app config (consumed via %env(...)% in config/) - nginx env — runtime template substitution into nginx.conf - php-fpm env — runtime tuning consumed by the itkdev/php8.4-fpm base Annotate .env with per-variable descriptions so it is the canonical, self-documenting Symfony example, and add dedicated production-example files for the nginx and php-fpm runtime surfaces. Variable names mirror what the image and templates actually consume — no APP_* prefix translation. Validated by the env-coverage check from the consolidation plan: every %env(...)% reference in config/ is declared in .env and every ${VAR} in the nginx template is declared in the nginx example. Co-Authored-By: Claude Opus 4.7 (1M context) --- .env | 75 +++++++++++++++++-- CHANGELOG.md | 3 + .../env.production.example | 46 ++++++++++++ infrastructure/nginx/env.production.example | 23 ++++++ 4 files changed, 139 insertions(+), 8 deletions(-) create mode 100644 infrastructure/display-api-service/env.production.example create mode 100644 infrastructure/nginx/env.production.example diff --git a/.env b/.env index f6b9edecb..f9e2f4e3b 100644 --- a/.env +++ b/.env @@ -1,5 +1,8 @@ +# Docker Compose project name (used as a prefix for container names in local dev). COMPOSE_PROJECT_NAME=display +# Hostname used by the local dev stack's reverse proxy. COMPOSE_DOMAIN=display.local.itkdev.dk +# itkdev-docker template identifier — selects the local compose layout. ITKDEV_TEMPLATE=symfony-6 # In all environments, the following files are loaded if they exist, @@ -18,12 +21,16 @@ ITKDEV_TEMPLATE=symfony-6 # https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration ###> symfony/framework-bundle ### +# Application environment (prod, dev, test). Override to 'prod' in .env.local on production hosts. APP_ENV=dev +# Secret key used for encryption and security-related operations (MUST BE CHANGED in .env.local for production). APP_SECRET=CHANGE_ME +# Comma-separated list of trusted proxy IPs for handling X-Forwarded headers. TRUSTED_PROXIES=127.0.0.1,REMOTE_ADDR ###< symfony/framework-bundle ### ###> doctrine/doctrine-bundle ### +# Database connection string for MariaDB/MySQL. # Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url # IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml # @@ -33,105 +40,157 @@ DATABASE_URL="mysql://db:db@mariadb:3306/db?serverVersion=10.11.5-MariaDB" ###< doctrine/doctrine-bundle ### ###> nelmio/cors-bundle ### -CORS_ALLOW_ORIGIN='^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$' +# Regular expression defining allowed origins for CORS requests. # Add allowed urls to .env.local as regex. # For development use: # CORS_ALLOW_ORIGIN="^.*" +CORS_ALLOW_ORIGIN='^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$' ###< nelmio/cors-bundle ### ###> lexik/jwt-authentication-bundle ### +# Path to JWT private key file for signing tokens. JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem +# Path to JWT public key file for verifying tokens. JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem +# Passphrase for JWT private key (MUST BE CHANGED in .env.local for production). JWT_PASSPHRASE=CHANGE_ME -# Default: 1 hour +# Time-to-live in seconds for JWT access tokens. Default: 3600 (1 hour). JWT_TOKEN_TTL=3600 -# Default: 15 days +# Time-to-live in seconds for screen JWT tokens. Default: 1296000 (15 days). JWT_SCREEN_TOKEN_TTL=1296000 ###< lexik/jwt-authentication-bundle ### ###> gesdinet/jwt-refresh-token-bundle ### -# Default: 2 hours +# Time-to-live in seconds for JWT refresh tokens. Default: 7200 (2 hours). JWT_REFRESH_TOKEN_TTL=7200 -# Default: 30 days +# Time-to-live in seconds for screen JWT refresh tokens. Default: 2592000 (30 days). JWT_SCREEN_REFRESH_TOKEN_TTL=2592000 ###< gesdinet/jwt-refresh-token-bundle ### ###> redis ### +# Prefix for Redis cache keys to avoid collisions across applications sharing a Redis instance. REDIS_CACHE_PREFIX=DisplayApiService +# Connection string for Redis cache server. REDIS_CACHE_DSN=redis://redis:6379/0 ###< redis ### ###> Http Client ### +# Maximum time in seconds the HTTP client waits for a connection. HTTP_CLIENT_TIMEOUT=5 +# Maximum total duration in seconds for an HTTP request including transfer. HTTP_CLIENT_MAX_DURATION=30 +# Log level for outgoing HTTP requests (debug, info, notice, warning, error). HTTP_CLIENT_LOG_LEVEL=error ###< Http Client ### ###> App ### +# Default date format for API responses (ISO 8601 with milliseconds). DEFAULT_DATE_FORMAT='Y-m-d\TH:i:s.v\Z' +# Time interval for activation code expiration (ISO 8601 duration format, P2D = 2 days). ACTIVATION_CODE_EXPIRE_INTERVAL=P2D +# Source for key vault configuration (ENVIRONMENT, FILE, etc.). KEY_VAULT_SOURCE=ENVIRONMENT +# JSON configuration for key vault when using ENVIRONMENT source. KEY_VAULT_JSON="{}" +# Enable tracking of screen information and statistics. TRACK_SCREEN_INFO=false +# Interval in seconds between screen info updates. TRACK_SCREEN_INFO_UPDATE_INTERVAL_SECONDS=300 +# Toggle the relations checksum optimisation for screen/playlist updates. RELATIONS_CHECKSUM_ENABLED=true ###< App ### ###> itk-dev/openid-connect-bundle ### # See docs/feed/openid-connect.md for variable explanations. -# internal provider +# internal provider (admin login) +# URL to OIDC provider's metadata/discovery endpoint. INTERNAL_OIDC_METADATA_URL=INTERNAL_OIDC_METADATA_URL +# Client ID registered with the OIDC provider. INTERNAL_OIDC_CLIENT_ID=INTERNAL_OIDC_CLIENT_ID +# Client secret for authentication with the OIDC provider. INTERNAL_OIDC_CLIENT_SECRET=INTERNAL_OIDC_CLIENT_SECRET +# Redirect URI for OIDC callback after authentication. INTERNAL_OIDC_REDIRECT_URI=INTERNAL_OIDC_REDIRECT_URI +# Leeway time in seconds for token validation clock skew tolerance. INTERNAL_OIDC_LEEWAY=30 +# Name of the claim containing user's name. INTERNAL_OIDC_CLAIM_NAME=navn +# Name of the claim containing user's email. INTERNAL_OIDC_CLAIM_EMAIL=email +# Name of the claim containing user's group memberships. INTERNAL_OIDC_CLAIM_GROUPS=groups -# external provider +# external provider (citizen login) +# URL to external OIDC provider's metadata/discovery endpoint. EXTERNAL_OIDC_METADATA_URL=EXTERNAL_OIDC_METADATA_URL +# Client ID registered with the external OIDC provider. EXTERNAL_OIDC_CLIENT_ID=EXTERNAL_OIDC_CLIENT_ID +# Client secret for authentication with the external OIDC provider. EXTERNAL_OIDC_CLIENT_SECRET=EXTERNAL_OIDC_CLIENT_SECRET +# Redirect URI for external OIDC callback after authentication. EXTERNAL_OIDC_REDIRECT_URI=EXTERNAL_OIDC_REDIRECT_URI +# Leeway time in seconds for external token validation clock skew tolerance. EXTERNAL_OIDC_LEEWAY=30 +# Salt used for hashing external OIDC user identifiers. EXTERNAL_OIDC_HASH_SALT= +# Name of the claim containing user's sign-in identifier. EXTERNAL_OIDC_CLAIM_ID=signinname -# cli redirect url +# Redirect URI for CLI-based OIDC authentication flow. OIDC_CLI_REDIRECT=APP_CLI_REDIRECT_URI ###< itk-dev/openid-connect-bundle ### ###> Calendar Api Feed Source ### # See docs/feed/calendar-api-feed.md for variable explanations. +# API endpoint for calendar location data. CALENDAR_API_FEED_SOURCE_LOCATION_ENDPOINT= +# API endpoint for calendar resource data. CALENDAR_API_FEED_SOURCE_RESOURCE_ENDPOINT= +# API endpoint for calendar event data. CALENDAR_API_FEED_SOURCE_EVENT_ENDPOINT= +# JSON object for custom field mappings in calendar feeds. CALENDAR_API_FEED_SOURCE_CUSTOM_MAPPINGS='{}' +# JSON object for modifying calendar event data. CALENDAR_API_FEED_SOURCE_EVENT_MODIFIERS='{}' +# Date format used by the calendar API source. CALENDAR_API_FEED_SOURCE_DATE_FORMAT= +# Timezone used by the calendar API source. CALENDAR_API_FEED_SOURCE_DATE_TIMEZONE= +# Cache expiration time in seconds for calendar feed data. CALENDAR_API_FEED_SOURCE_CACHE_EXPIRE_SECONDS=300 ###< Calendar Api Feed Source ### ###> Event Database Api V2 Feed Type ### +# Cache expiration time in seconds for event database API v2 responses. EVENTDATABASE_API_V2_CACHE_EXPIRE_SECONDS=300 ###< Event Database Api V2 Feed Type ### ###> Admin configuration ### +# API key for Rejseplanen (Danish journey planner) integration. ADMIN_REJSEPLANEN_APIKEY= +# Enable display of screen online/offline status in admin interface. ADMIN_SHOW_SCREEN_STATUS=false +# Enable touch button regions feature in admin interface. ADMIN_TOUCH_BUTTON_REGIONS=false +# JSON array defining available login methods for admin interface. ADMIN_LOGIN_METHODS='[{"type":"username-password","enabled":true,"provider":"username-password","label":""}]' +# Enable enhanced preview mode with additional features. ADMIN_ENHANCED_PREVIEW=false ###< Admin configuration ### ###> Client configuration ### +# Timeout in milliseconds for checking login status. CLIENT_LOGIN_CHECK_TIMEOUT=20000 +# Timeout in milliseconds before attempting to refresh authentication token. 300000 = 5 minutes. CLIENT_REFRESH_TOKEN_TIMEOUT=300000 +# Interval in milliseconds for checking release timestamp updates. 600000 = 10 minutes. CLIENT_RELEASE_TIMESTAMP_INTERVAL_TIMEOUT=600000 +# Interval in milliseconds for running content scheduling checks. 60000 = 1 minute. CLIENT_SCHEDULING_INTERVAL=60000 +# Interval in milliseconds for pulling new content from server. 90000 = 1.5 minutes. CLIENT_PULL_STRATEGY_INTERVAL=90000 +# JSON configuration for client color scheme with location coordinates. CLIENT_COLOR_SCHEME='{"type":"library","lat":56.0,"lng":10.0}' +# Enable debug mode for client-side logging and diagnostics. CLIENT_DEBUG=false ###< Client configuration ### diff --git a/CHANGELOG.md b/CHANGELOG.md index d7040a316..4d66826ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,9 @@ All notable changes to this project will be documented in this file. - Removed fixture length check from test. - Added vitest for frontend unit tests. - Updated infrastructure and image build for mono-repo. +- Documented the operator-facing configuration surface: annotated `.env` for + the Symfony app, and added production-example files for the nginx and + PHP-FPM runtime environments. ### NB! Prior to 3.x the project was split into separate repositories diff --git a/infrastructure/display-api-service/env.production.example b/infrastructure/display-api-service/env.production.example new file mode 100644 index 000000000..74bcac8d3 --- /dev/null +++ b/infrastructure/display-api-service/env.production.example @@ -0,0 +1,46 @@ +# PHP-FPM runtime environment for the os2display api service image. +# +# OS-level / runtime tuning consumed at container start by the +# itkdev/php8.4-fpm base image's entrypoint. Pass these via the operator's +# compose `env_file:` (or per-variable `environment:`). Symfony application +# config is unrelated and lives in .env / .env.local. + +### +# General PHP limits +### +# Maximum time in seconds a request is allowed to run. Default: 30. Match +# this with proxy timeouts upstream of the container. +PHP_MAX_EXECUTION_TIME=30 +# Memory cap per PHP process. Default: 128M. +PHP_MEMORY_LIMIT=256M +# Total POST body size accepted by PHP. Default: 8M. Raise in lockstep with +# NGINX_MAX_BODY_SIZE. +PHP_POST_MAX_SIZE=140M +# Maximum size of an uploaded file. Default: 2M. +PHP_UPLOAD_MAX_FILESIZE=128M +# Default timezone. Default: Europe/Copenhagen. +PHP_TIMEZONE=Europe/Copenhagen + +### +# OpCache — keep enabled and freeze timestamp validation in production. +### +PHP_OPCACHE_ENABLED=1 +PHP_OPCACHE_VALIDATE_TIMESTAMPS=0 +PHP_OPCACHE_MAX_ACCELERATED_FILES=20000 +PHP_OPCACHE_MEMORY_CONSUMPTION=256 + +### +# php-fpm process manager — tune to host capacity. +### +# 'static' (predictable load), 'dynamic', or 'ondemand'. +PHP_PM_TYPE=dynamic +# Maximum total worker processes. +PHP_PM_MAX_CHILDREN=24 +# Workers spawned at startup. +PHP_PM_START_SERVERS=5 +# Spare-worker thresholds. +PHP_PM_MIN_SPARE_SERVERS=5 +PHP_PM_MAX_SPARE_SERVERS=10 +# Recycle a worker after N requests (0 = never). Use a finite value if a +# memory leak is suspected. +PHP_PM_MAX_REQUESTS=0 diff --git a/infrastructure/nginx/env.production.example b/infrastructure/nginx/env.production.example new file mode 100644 index 000000000..73cdc2fc6 --- /dev/null +++ b/infrastructure/nginx/env.production.example @@ -0,0 +1,23 @@ +# nginx runtime environment for the os2display nginx image. +# +# These values are substituted by the nginx-unprivileged entrypoint into +# /etc/nginx/conf.d/default.conf at container start. Pass them via the +# operator's compose `env_file:` (or per-variable `environment:`). +# +# Application config (Symfony) is unrelated and lives in .env / .env.local. + +# REQUIRED — upstream php-fpm endpoint (host:port). Resolves on the compose +# network; e.g. 'phpfpm:9000' if the api service is named 'phpfpm'. +NGINX_FPM_SERVICE= + +# Listen port inside the container. Default: 8080. +NGINX_PORT=8080 + +# Maximum request body nginx accepts. Default: 10m. Raise in lockstep with +# PHP_POST_MAX_SIZE if operators upload media via the API. +NGINX_MAX_BODY_SIZE=140m + +# CIDR of the trusted reverse proxy used to populate $remote_addr from +# X-Forwarded-For. Default: 172.17.0.0/16 (single docker bridge). Widen +# to 172.16.0.0/12 to cover the full RFC1918 docker range. +NGINX_SET_REAL_IP_FROM=172.16.0.0/12 From f427131e87a3883a661ac732cdd15d317dfa6e2d Mon Sep 17 00:00:00 2001 From: turegjorup Date: Tue, 28 Apr 2026 14:03:06 +0200 Subject: [PATCH 20/59] 5402: Add CI check that enforces env coverage scripts/check-env-coverage.sh validates the documentation surface introduced in the previous commit: 1. Every %env(...)% read in config/ is declared in .env. 2. Every variable in .env, the nginx example, and the PHP example has a preceding description. 3. Every ${VAR} in the nginx template is declared in the nginx env.production.example. 4. Every PHP_* env set in the API Dockerfile's production stage is declared in the PHP env.production.example. Wired into the existing 'coding-standards:check' task and a dedicated GitHub Actions workflow that runs on push to develop and on pull requests, using the official go-task/setup-task action. Also fills in five PHP_OPCACHE_* and PHP_PM_*_SPARE_SERVERS variable descriptions that check (2) caught as missing. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/env_coverage.yaml | 23 ++++ Taskfile.yml | 6 + .../env.production.example | 7 +- scripts/check-env-coverage.sh | 103 ++++++++++++++++++ 4 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/env_coverage.yaml create mode 100755 scripts/check-env-coverage.sh diff --git a/.github/workflows/env_coverage.yaml b/.github/workflows/env_coverage.yaml new file mode 100644 index 000000000..5a6dc18f8 --- /dev/null +++ b/.github/workflows/env_coverage.yaml @@ -0,0 +1,23 @@ +--- +on: + push: + branches: + - develop + pull_request: + +name: Env coverage + +jobs: + env-coverage: + name: Validate env documentation coverage + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Install task + uses: go-task/setup-task@v2 + with: + version: 3.x + + - name: Run coding-standards:env:check + run: task coding-standards:env:check diff --git a/Taskfile.yml b/Taskfile.yml index 0dbc4dc90..5a054f12c 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -108,6 +108,7 @@ tasks: - task coding-standards:php:check - task coding-standards:markdown:check - task coding-standards:yml:check + - task coding-standards:env:check coding-standards:apply: desc: "Apply coding standards." @@ -128,6 +129,11 @@ tasks: cmds: - docker compose run --rm --volume "$PWD:/md" prettier '**/*.{yml,yaml}' --check + coding-standards:env:check: + desc: Validate that operator-facing env vars are documented (.env, nginx). + cmds: + - sh scripts/check-env-coverage.sh + coding-standards:assets:apply: desc: Apply coding standards for assets cmds: diff --git a/infrastructure/display-api-service/env.production.example b/infrastructure/display-api-service/env.production.example index 74bcac8d3..15194b3fb 100644 --- a/infrastructure/display-api-service/env.production.example +++ b/infrastructure/display-api-service/env.production.example @@ -24,9 +24,13 @@ PHP_TIMEZONE=Europe/Copenhagen ### # OpCache — keep enabled and freeze timestamp validation in production. ### +# Enable OpCache. Default: 1. PHP_OPCACHE_ENABLED=1 +# Skip mtime checks on cached scripts (1 = check, 0 = freeze). Production: 0. PHP_OPCACHE_VALIDATE_TIMESTAMPS=0 +# Maximum number of scripts kept in OpCache. PHP_OPCACHE_MAX_ACCELERATED_FILES=20000 +# Memory budget for OpCache, in MB. PHP_OPCACHE_MEMORY_CONSUMPTION=256 ### @@ -38,8 +42,9 @@ PHP_PM_TYPE=dynamic PHP_PM_MAX_CHILDREN=24 # Workers spawned at startup. PHP_PM_START_SERVERS=5 -# Spare-worker thresholds. +# Lower bound for idle spare workers. PHP_PM_MIN_SPARE_SERVERS=5 +# Upper bound for idle spare workers. PHP_PM_MAX_SPARE_SERVERS=10 # Recycle a worker after N requests (0 = never). Use a finite value if a # memory leak is suspected. diff --git a/scripts/check-env-coverage.sh b/scripts/check-env-coverage.sh new file mode 100755 index 000000000..4f50aac18 --- /dev/null +++ b/scripts/check-env-coverage.sh @@ -0,0 +1,103 @@ +#!/bin/sh +# Validate that the operator-facing configuration surface is fully documented: +# 1. Every %env(...)% read in config/ is declared in .env. +# 2. Every variable in .env, in the nginx example, and in the PHP example +# has a preceding description comment. +# 3. Every ${VAR} in the nginx template is declared in +# infrastructure/nginx/env.production.example. +# 4. Every PHP_* env set in the API Dockerfile's production stage is +# declared in infrastructure/display-api-service/env.production.example. +set -e + +REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" +cd "$REPO_ROOT" + +NGINX_EXAMPLE=infrastructure/nginx/env.production.example +PHP_EXAMPLE=infrastructure/display-api-service/env.production.example +NGINX_TEMPLATE=infrastructure/nginx/etc/templates/default.conf.template +API_DOCKERFILE=infrastructure/display-api-service/Dockerfile + +# Vars consumed via %env() but intentionally not part of the .env contract +# (e.g. injected by tooling, not Symfony-defined defaults). +SYMFONY_ALLOW_LIST="VAR_DUMPER_SERVER" + +failed=0 +tmp=$(mktemp -d) +trap 'rm -rf "$tmp"' EXIT + +# Print variables that lack a preceding description comment. +# Recipe markers (###> ... ###) and blank lines reset the "had a comment" flag. +unannotated_vars() { + awk ' + # ### must be tested before # so recipe markers are not treated as comments. + /^###/ { last_was_comment = 0; next } + /^#/ { last_was_comment = 1; next } + /^$/ { last_was_comment = 0; next } + /^[A-Z_][A-Z0-9_]*=/ { + name = $0; sub("=.*", "", name) + if (!last_was_comment) print name + last_was_comment = 0 + } + ' "$1" +} + +# Print KEY= keys declared in a dotenv-style file. +declared_keys() { + grep -E '^[A-Z_][A-Z0-9_]*=' "$1" | cut -d= -f1 | sort -u +} + +# --- Check 1: %env() reads in config/ vs declarations in .env ----------------- +grep -rhoE '%env\(([a-z]+:)*[A-Z_][A-Z0-9_]*\)%' config/ \ + | sed -E 's/%env\(([a-z]+:)*([A-Z_][A-Z0-9_]*)\)%/\2/' \ + | sort -u > "$tmp/symfony-reads" +declared_keys .env > "$tmp/env-declares" +echo "$SYMFONY_ALLOW_LIST" | tr ' ' '\n' | sort -u > "$tmp/symfony-allow" + +undocumented=$(comm -23 "$tmp/symfony-reads" "$tmp/env-declares" \ + | comm -23 - "$tmp/symfony-allow") +if [ -n "$undocumented" ]; then + echo "FAIL: %env(...)% reads in config/ not declared in .env:" + echo "$undocumented" | sed 's/^/ /' + failed=1 +fi + +# --- Check 2: every variable has a preceding description comment -------------- +for f in .env "$NGINX_EXAMPLE" "$PHP_EXAMPLE"; do + unannotated=$(unannotated_vars "$f") + if [ -n "$unannotated" ]; then + echo "FAIL: variables in $f without preceding description:" + echo "$unannotated" | sed 's/^/ /' + failed=1 + fi +done + +# --- Check 3: nginx template ${VAR} vs nginx example -------------------------- +grep -hoE '\$\{[A-Z_][A-Z0-9_]*\}' "$NGINX_TEMPLATE" \ + | sed -E 's/[${}]//g' | sort -u > "$tmp/nginx-template-uses" +declared_keys "$NGINX_EXAMPLE" > "$tmp/nginx-example-declares" + +nginx_missing=$(comm -23 "$tmp/nginx-template-uses" "$tmp/nginx-example-declares") +if [ -n "$nginx_missing" ]; then + echo "FAIL: nginx template variables not declared in $NGINX_EXAMPLE:" + echo "$nginx_missing" | sed 's/^/ /' + failed=1 +fi + +# --- Check 4: PHP_* keys in API Dockerfile prod stage vs PHP example ---------- +# Extract the ENV block in the production stage (between the prod FROM and the +# next USER directive), then pick PHP_* keys. +awk '/^FROM itkdev\/php8\.4-fpm:alpine *$/,/^USER /' "$API_DOCKERFILE" \ + | grep -oE 'PHP_[A-Z0-9_]+=' | sed 's/=$//' | sort -u > "$tmp/dockerfile-php" +declared_keys "$PHP_EXAMPLE" > "$tmp/php-example-declares" + +php_missing=$(comm -23 "$tmp/dockerfile-php" "$tmp/php-example-declares") +if [ -n "$php_missing" ]; then + echo "FAIL: PHP_* env set in $API_DOCKERFILE but not declared in $PHP_EXAMPLE:" + echo "$php_missing" | sed 's/^/ /' + failed=1 +fi + +if [ "$failed" -eq 0 ]; then + echo "PASS: env coverage OK" +fi +exit "$failed" From c2982e89bc8c257d75a4992a01e7652b9812a5c1 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Wed, 29 Apr 2026 10:29:07 +0200 Subject: [PATCH 21/59] 5402: Make RC builds tag-only and adopt rolling latest tag RC images are now produced by tagging on a release branch (e.g. tag '3.0.0-RC1' on release/3.0.0), not by every push to release/*. This keeps RC builds explicit and reviewable instead of running on every push to a release branch. Add 'flavor: latest=auto' so non-prerelease semver tags ('3.0.0') also update ':latest', while pre-release semver tags ('3.0.0-RC1') do not. This is the metadata-action default but is set explicitly with a comment so the behavior is obvious. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/build-images.yml | 31 ++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build-images.yml b/.github/workflows/build-images.yml index d0a51098d..91bdc9aca 100644 --- a/.github/workflows/build-images.yml +++ b/.github/workflows/build-images.yml @@ -5,7 +5,6 @@ on: push: branches: - develop - - "release/*" tags: - "*" @@ -37,13 +36,18 @@ jobs: with: images: ghcr.io/os2display/display-api-service tags: | - # develop branch → 'develop' (skipped on release/* pushes) - type=ref,event=branch,enable=${{ !startsWith(github.ref, 'refs/heads/release/') }} - # release/X.Y.Z branch → 'X.Y.Z-rc' - type=match,pattern=release/(\d+\.\d+\.\d+),group=1,suffix=-rc - # version tags → 'X.Y.Z' + # develop branch → 'develop' + type=ref,event=branch + # any tag → tag literal (e.g. '3.0.0', '3.0.0-RC1' for release + # candidates cut from release/X.Y.Z) type=ref,event=tag + # also emit canonical semver tag for valid semver-shaped tags, + # so '3.0.0' and '3.0.0-RC1' both produce a properly-typed entry. type=semver,pattern={{version}} + # latest=auto adds ':latest' only for non-prerelease semver tags, + # so '3.0.0' updates ':latest' but '3.0.0-RC1' does not. + flavor: | + latest=auto - name: Build and push (API) uses: docker/build-push-action@v7 @@ -54,7 +58,7 @@ jobs: repository-root=. # Use the version computed by docker/metadata-action so APP_VERSION # matches the image tag for every trigger ('develop' / '3.0.0' / - # '3.0.0-rc'), instead of github.ref_name which would be 'release/3.0.0'. + # '3.0.0-RC1') without bespoke string handling per trigger. build-args: | APP_VERSION=${{ steps.meta-api.outputs.version }} APP_RELEASE_VERSION=${{ steps.meta-api.outputs.version }} @@ -77,13 +81,16 @@ jobs: with: images: ghcr.io/os2display/display-api-service-nginx tags: | - # develop branch → 'develop' (skipped on release/* pushes) - type=ref,event=branch,enable=${{ !startsWith(github.ref, 'refs/heads/release/') }} - # release/X.Y.Z branch → 'X.Y.Z-rc' - type=match,pattern=release/(\d+\.\d+\.\d+),group=1,suffix=-rc - # version tags → 'X.Y.Z' + # develop branch → 'develop' + type=ref,event=branch + # any tag → tag literal (e.g. '3.0.0', '3.0.0-RC1' for release + # candidates cut from release/X.Y.Z) type=ref,event=tag + # also emit canonical semver tag for valid semver-shaped tags, + # so '3.0.0' and '3.0.0-RC1' both produce a properly-typed entry. type=semver,pattern={{version}} + flavor: | + latest=auto - name: Build and push (Nginx) uses: docker/build-push-action@v7 From c008e8791a2ded80076cb208501d93408887ff93 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Wed, 29 Apr 2026 10:49:58 +0200 Subject: [PATCH 22/59] 5402: Narrow scope to .env documentation only Drop the dedicated nginx and PHP-FPM env.production.example files and the corresponding script checks. The operator-facing configuration contract for those two surfaces is documented adjacent to the image build (Dockerfile ENV blocks and the nginx template) rather than as duplicated example files. The .env / config/ coverage check remains: every %env(...)% read in config/ is declared in .env, and every variable in .env has a preceding description. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 5 +- Taskfile.yml | 2 +- .../env.production.example | 51 ---------- infrastructure/nginx/env.production.example | 23 ----- scripts/check-env-coverage.sh | 95 +++++-------------- 5 files changed, 26 insertions(+), 150 deletions(-) delete mode 100644 infrastructure/display-api-service/env.production.example delete mode 100644 infrastructure/nginx/env.production.example diff --git a/CHANGELOG.md b/CHANGELOG.md index ee9d945f1..d64e65aa9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,9 +37,8 @@ All notable changes to this project will be documented in this file. - Added vitest for frontend unit tests. - Updated infrastructure and image build for mono-repo. - Fixed nginx static-file location to fall back to PHP so LiipImagineBundle can generate missing thumbnails (#370). -- Documented the operator-facing configuration surface: annotated `.env` for - the Symfony app, and added production-example files for the nginx and - PHP-FPM runtime environments. +- Annotated `.env` so it serves as the canonical, self-documenting Symfony + env example, with a CI check that enforces it stays in sync with `config/`. ### NB! Prior to 3.x the project was split into separate repositories diff --git a/Taskfile.yml b/Taskfile.yml index 5a054f12c..3a198f3d2 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -130,7 +130,7 @@ tasks: - docker compose run --rm --volume "$PWD:/md" prettier '**/*.{yml,yaml}' --check coding-standards:env:check: - desc: Validate that operator-facing env vars are documented (.env, nginx). + desc: Validate that every Symfony env variable in config/ is documented in .env. cmds: - sh scripts/check-env-coverage.sh diff --git a/infrastructure/display-api-service/env.production.example b/infrastructure/display-api-service/env.production.example deleted file mode 100644 index 15194b3fb..000000000 --- a/infrastructure/display-api-service/env.production.example +++ /dev/null @@ -1,51 +0,0 @@ -# PHP-FPM runtime environment for the os2display api service image. -# -# OS-level / runtime tuning consumed at container start by the -# itkdev/php8.4-fpm base image's entrypoint. Pass these via the operator's -# compose `env_file:` (or per-variable `environment:`). Symfony application -# config is unrelated and lives in .env / .env.local. - -### -# General PHP limits -### -# Maximum time in seconds a request is allowed to run. Default: 30. Match -# this with proxy timeouts upstream of the container. -PHP_MAX_EXECUTION_TIME=30 -# Memory cap per PHP process. Default: 128M. -PHP_MEMORY_LIMIT=256M -# Total POST body size accepted by PHP. Default: 8M. Raise in lockstep with -# NGINX_MAX_BODY_SIZE. -PHP_POST_MAX_SIZE=140M -# Maximum size of an uploaded file. Default: 2M. -PHP_UPLOAD_MAX_FILESIZE=128M -# Default timezone. Default: Europe/Copenhagen. -PHP_TIMEZONE=Europe/Copenhagen - -### -# OpCache — keep enabled and freeze timestamp validation in production. -### -# Enable OpCache. Default: 1. -PHP_OPCACHE_ENABLED=1 -# Skip mtime checks on cached scripts (1 = check, 0 = freeze). Production: 0. -PHP_OPCACHE_VALIDATE_TIMESTAMPS=0 -# Maximum number of scripts kept in OpCache. -PHP_OPCACHE_MAX_ACCELERATED_FILES=20000 -# Memory budget for OpCache, in MB. -PHP_OPCACHE_MEMORY_CONSUMPTION=256 - -### -# php-fpm process manager — tune to host capacity. -### -# 'static' (predictable load), 'dynamic', or 'ondemand'. -PHP_PM_TYPE=dynamic -# Maximum total worker processes. -PHP_PM_MAX_CHILDREN=24 -# Workers spawned at startup. -PHP_PM_START_SERVERS=5 -# Lower bound for idle spare workers. -PHP_PM_MIN_SPARE_SERVERS=5 -# Upper bound for idle spare workers. -PHP_PM_MAX_SPARE_SERVERS=10 -# Recycle a worker after N requests (0 = never). Use a finite value if a -# memory leak is suspected. -PHP_PM_MAX_REQUESTS=0 diff --git a/infrastructure/nginx/env.production.example b/infrastructure/nginx/env.production.example deleted file mode 100644 index 73cdc2fc6..000000000 --- a/infrastructure/nginx/env.production.example +++ /dev/null @@ -1,23 +0,0 @@ -# nginx runtime environment for the os2display nginx image. -# -# These values are substituted by the nginx-unprivileged entrypoint into -# /etc/nginx/conf.d/default.conf at container start. Pass them via the -# operator's compose `env_file:` (or per-variable `environment:`). -# -# Application config (Symfony) is unrelated and lives in .env / .env.local. - -# REQUIRED — upstream php-fpm endpoint (host:port). Resolves on the compose -# network; e.g. 'phpfpm:9000' if the api service is named 'phpfpm'. -NGINX_FPM_SERVICE= - -# Listen port inside the container. Default: 8080. -NGINX_PORT=8080 - -# Maximum request body nginx accepts. Default: 10m. Raise in lockstep with -# PHP_POST_MAX_SIZE if operators upload media via the API. -NGINX_MAX_BODY_SIZE=140m - -# CIDR of the trusted reverse proxy used to populate $remote_addr from -# X-Forwarded-For. Default: 172.17.0.0/16 (single docker bridge). Widen -# to 172.16.0.0/12 to cover the full RFC1918 docker range. -NGINX_SET_REAL_IP_FROM=172.16.0.0/12 diff --git a/scripts/check-env-coverage.sh b/scripts/check-env-coverage.sh index 4f50aac18..d040e2d1a 100755 --- a/scripts/check-env-coverage.sh +++ b/scripts/check-env-coverage.sh @@ -1,99 +1,50 @@ #!/bin/sh -# Validate that the operator-facing configuration surface is fully documented: +# Validate that .env documents every Symfony env variable consumed in config/: # 1. Every %env(...)% read in config/ is declared in .env. -# 2. Every variable in .env, in the nginx example, and in the PHP example -# has a preceding description comment. -# 3. Every ${VAR} in the nginx template is declared in -# infrastructure/nginx/env.production.example. -# 4. Every PHP_* env set in the API Dockerfile's production stage is -# declared in infrastructure/display-api-service/env.production.example. +# 2. Every variable in .env has a preceding description comment. set -e REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" cd "$REPO_ROOT" -NGINX_EXAMPLE=infrastructure/nginx/env.production.example -PHP_EXAMPLE=infrastructure/display-api-service/env.production.example -NGINX_TEMPLATE=infrastructure/nginx/etc/templates/default.conf.template -API_DOCKERFILE=infrastructure/display-api-service/Dockerfile - # Vars consumed via %env() but intentionally not part of the .env contract # (e.g. injected by tooling, not Symfony-defined defaults). -SYMFONY_ALLOW_LIST="VAR_DUMPER_SERVER" +ALLOW_LIST="VAR_DUMPER_SERVER" failed=0 tmp=$(mktemp -d) trap 'rm -rf "$tmp"' EXIT -# Print variables that lack a preceding description comment. -# Recipe markers (###> ... ###) and blank lines reset the "had a comment" flag. -unannotated_vars() { - awk ' - # ### must be tested before # so recipe markers are not treated as comments. - /^###/ { last_was_comment = 0; next } - /^#/ { last_was_comment = 1; next } - /^$/ { last_was_comment = 0; next } - /^[A-Z_][A-Z0-9_]*=/ { - name = $0; sub("=.*", "", name) - if (!last_was_comment) print name - last_was_comment = 0 - } - ' "$1" -} - -# Print KEY= keys declared in a dotenv-style file. -declared_keys() { - grep -E '^[A-Z_][A-Z0-9_]*=' "$1" | cut -d= -f1 | sort -u -} - -# --- Check 1: %env() reads in config/ vs declarations in .env ----------------- +# Check 1 — every %env(...)% read in config/ is declared in .env grep -rhoE '%env\(([a-z]+:)*[A-Z_][A-Z0-9_]*\)%' config/ \ | sed -E 's/%env\(([a-z]+:)*([A-Z_][A-Z0-9_]*)\)%/\2/' \ | sort -u > "$tmp/symfony-reads" -declared_keys .env > "$tmp/env-declares" -echo "$SYMFONY_ALLOW_LIST" | tr ' ' '\n' | sort -u > "$tmp/symfony-allow" +grep -E '^[A-Z_][A-Z0-9_]*=' .env | cut -d= -f1 | sort -u > "$tmp/env-declares" +echo "$ALLOW_LIST" | tr ' ' '\n' | sort -u > "$tmp/allow" -undocumented=$(comm -23 "$tmp/symfony-reads" "$tmp/env-declares" \ - | comm -23 - "$tmp/symfony-allow") +undocumented=$(comm -23 "$tmp/symfony-reads" "$tmp/env-declares" | comm -23 - "$tmp/allow") if [ -n "$undocumented" ]; then echo "FAIL: %env(...)% reads in config/ not declared in .env:" echo "$undocumented" | sed 's/^/ /' failed=1 fi -# --- Check 2: every variable has a preceding description comment -------------- -for f in .env "$NGINX_EXAMPLE" "$PHP_EXAMPLE"; do - unannotated=$(unannotated_vars "$f") - if [ -n "$unannotated" ]; then - echo "FAIL: variables in $f without preceding description:" - echo "$unannotated" | sed 's/^/ /' - failed=1 - fi -done - -# --- Check 3: nginx template ${VAR} vs nginx example -------------------------- -grep -hoE '\$\{[A-Z_][A-Z0-9_]*\}' "$NGINX_TEMPLATE" \ - | sed -E 's/[${}]//g' | sort -u > "$tmp/nginx-template-uses" -declared_keys "$NGINX_EXAMPLE" > "$tmp/nginx-example-declares" - -nginx_missing=$(comm -23 "$tmp/nginx-template-uses" "$tmp/nginx-example-declares") -if [ -n "$nginx_missing" ]; then - echo "FAIL: nginx template variables not declared in $NGINX_EXAMPLE:" - echo "$nginx_missing" | sed 's/^/ /' - failed=1 -fi - -# --- Check 4: PHP_* keys in API Dockerfile prod stage vs PHP example ---------- -# Extract the ENV block in the production stage (between the prod FROM and the -# next USER directive), then pick PHP_* keys. -awk '/^FROM itkdev\/php8\.4-fpm:alpine *$/,/^USER /' "$API_DOCKERFILE" \ - | grep -oE 'PHP_[A-Z0-9_]+=' | sed 's/=$//' | sort -u > "$tmp/dockerfile-php" -declared_keys "$PHP_EXAMPLE" > "$tmp/php-example-declares" - -php_missing=$(comm -23 "$tmp/dockerfile-php" "$tmp/php-example-declares") -if [ -n "$php_missing" ]; then - echo "FAIL: PHP_* env set in $API_DOCKERFILE but not declared in $PHP_EXAMPLE:" - echo "$php_missing" | sed 's/^/ /' +# Check 2 — every variable in .env has a preceding description comment +unannotated=$(awk ' + # Order matters: ### must be tested before # so recipe markers are not + # treated as descriptions. + /^###/ { last_was_comment = 0; next } + /^#/ { last_was_comment = 1; next } + /^$/ { last_was_comment = 0; next } + /^[A-Z_][A-Z0-9_]*=/ { + name = $0; sub("=.*", "", name) + if (!last_was_comment) print name + last_was_comment = 0 + } +' .env) +if [ -n "$unannotated" ]; then + echo "FAIL: variables in .env without preceding description:" + echo "$unannotated" | sed 's/^/ /' failed=1 fi From 3763abd3f6648e1bbd457ff38014018bd2194b30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ture=20Gj=C3=B8rup?= Date: Wed, 29 Apr 2026 10:59:47 +0200 Subject: [PATCH 23/59] Update .env Co-authored-by: Troels Ugilt Jensen <6103205+tuj@users.noreply.github.com> --- .env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env b/.env index f9e2f4e3b..a9d49f851 100644 --- a/.env +++ b/.env @@ -2,7 +2,7 @@ COMPOSE_PROJECT_NAME=display # Hostname used by the local dev stack's reverse proxy. COMPOSE_DOMAIN=display.local.itkdev.dk -# itkdev-docker template identifier — selects the local compose layout. +# itkdev-docker template identifier. ITKDEV_TEMPLATE=symfony-6 # In all environments, the following files are loaded if they exist, From ef0218ae8ff1575049b1521a36a6139aceaf58ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ture=20Gj=C3=B8rup?= Date: Wed, 29 Apr 2026 11:00:00 +0200 Subject: [PATCH 24/59] Update .env Co-authored-by: Troels Ugilt Jensen <6103205+tuj@users.noreply.github.com> --- .env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env b/.env index a9d49f851..8d17935e6 100644 --- a/.env +++ b/.env @@ -96,7 +96,7 @@ KEY_VAULT_JSON="{}" TRACK_SCREEN_INFO=false # Interval in seconds between screen info updates. TRACK_SCREEN_INFO_UPDATE_INTERVAL_SECONDS=300 -# Toggle the relations checksum optimisation for screen/playlist updates. +# Toggle the relations checksum optimisation for content updates. RELATIONS_CHECKSUM_ENABLED=true ###< App ### From f2a27010b2e896a1c38c04c28feedf7d465a5767 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Wed, 29 Apr 2026 11:04:13 +0200 Subject: [PATCH 25/59] 5402: Align nginx image env-var contract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Finalize the nginx image's operator-facing env-var surface so the compose side has a single name per concept: NGINX_FPM_SERVICE=os2display (host only — was host:port combined) NGINX_FPM_PORT=9000 (new — was embedded in NGINX_FPM_SERVICE) NGINX_PORT=8080 (unchanged) NGINX_MAX_BODY_SIZE=140m (was 10m) NGINX_SET_REAL_IP_FROM=172.16.0.0/12 (was 172.17.0.0/16) The /16 → /12 widening covers the full RFC1918 docker bridge range (172.16-172.31), so multi-network compose stacks no longer need to override the default. The 140m upload cap matches the planned PHP_POST_MAX_SIZE / NGINX_MAX_BODY_SIZE alignment for media uploads. The production template's upstream block now references ${NGINX_FPM_SERVICE}:${NGINX_FPM_PORT} explicitly. The dev compose stack uses its own template under .docker/templates/ and is unaffected by this change. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 2 ++ infrastructure/nginx/Dockerfile | 6 ++++-- infrastructure/nginx/etc/templates/default.conf.template | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ac94d66d..9350fa709 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,8 @@ All notable changes to this project will be documented in this file. - Added vitest for frontend unit tests. - Updated infrastructure and image build for mono-repo. - Fixed nginx static-file location to fall back to PHP so LiipImagineBundle can generate missing thumbnails (#370). +- Aligned the nginx image env-var contract: split `NGINX_FPM_SERVICE` and + `NGINX_FPM_PORT`, raised upload cap and trusted-proxy CIDR defaults. ### NB! Prior to 3.x the project was split into separate repositories diff --git a/infrastructure/nginx/Dockerfile b/infrastructure/nginx/Dockerfile index 5bf5c00f7..e20fd93bf 100644 --- a/infrastructure/nginx/Dockerfile +++ b/infrastructure/nginx/Dockerfile @@ -71,8 +71,10 @@ ARG GID=101 ENV APP_CLIENT_PATH=/app \ APP_API_PATH=/var/www/html \ NGINX_PORT=8080 \ - NGINX_SET_REAL_IP_FROM=172.17.0.0/16 \ - NGINX_MAX_BODY_SIZE=10m + NGINX_FPM_SERVICE=os2display \ + NGINX_FPM_PORT=9000 \ + NGINX_MAX_BODY_SIZE=140m \ + NGINX_SET_REAL_IP_FROM=172.16.0.0/12 WORKDIR ${APP_API_PATH} diff --git a/infrastructure/nginx/etc/templates/default.conf.template b/infrastructure/nginx/etc/templates/default.conf.template index 7902373a6..7fddb4aba 100644 --- a/infrastructure/nginx/etc/templates/default.conf.template +++ b/infrastructure/nginx/etc/templates/default.conf.template @@ -1,5 +1,5 @@ upstream api_backend { - server ${NGINX_FPM_SERVICE}; + server ${NGINX_FPM_SERVICE}:${NGINX_FPM_PORT}; keepalive 16; } From cdca10622df35bb520b34eaa83c311f78ef70828 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Wed, 29 Apr 2026 12:10:53 +0200 Subject: [PATCH 26/59] 5402: Update dev compose for split FPM_SERVICE/FPM_PORT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After PR #409 unified the dev and prod nginx templates, the dev compose stack also consumes the production template — but it still passed NGINX_FPM_SERVICE as a combined 'host:port' string. With the template change in this PR ('server ${NGINX_FPM_SERVICE}:${NGINX_FPM_PORT};'), that produced a malformed 'display-phpfpm-1:9000:9000' upstream and nginx failed to start, breaking the Playwright CI run. Split the value in both docker-compose.yml and docker-compose.server.yml. Co-Authored-By: Claude Opus 4.7 (1M context) --- docker-compose.server.yml | 3 ++- docker-compose.yml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docker-compose.server.yml b/docker-compose.server.yml index d9a04931f..b261c9dd5 100644 --- a/docker-compose.server.yml +++ b/docker-compose.server.yml @@ -33,7 +33,8 @@ services: - ./infrastructure/nginx/etc/nginx.conf:/etc/nginx/nginx.conf:ro - .:/app environment: - NGINX_FPM_SERVICE: ${COMPOSE_PROJECT_NAME}-phpfpm-1:9000 + NGINX_FPM_SERVICE: ${COMPOSE_PROJECT_NAME}-phpfpm-1 + NGINX_FPM_PORT: 9000 NGINX_WEB_ROOT: /app/public NGINX_PORT: 8080 NGINX_MAX_BODY_SIZE: 5M diff --git a/docker-compose.yml b/docker-compose.yml index d2b8da507..be33f4ecc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -61,7 +61,8 @@ services: - ./infrastructure/nginx/etc/nginx.conf:/etc/nginx/nginx.conf:ro - .:/app environment: - NGINX_FPM_SERVICE: ${COMPOSE_PROJECT_NAME}-phpfpm-1:9000 + NGINX_FPM_SERVICE: ${COMPOSE_PROJECT_NAME}-phpfpm-1 + NGINX_FPM_PORT: 9000 NGINX_WEB_ROOT: /app/public NGINX_PORT: 8080 NGINX_MAX_BODY_SIZE: 5M From 4baeed2722252871b508f883a5ae1f23de585701 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Wed, 29 Apr 2026 12:16:14 +0200 Subject: [PATCH 27/59] 5402: Add nginx healthcheck and gate playwright on it Future nginx config errors now block playwright instead of surfacing as DNS failures. Co-Authored-By: Claude Opus 4.7 (1M context) --- docker-compose.override.yml | 3 ++- docker-compose.server.yml | 13 +++++++++++++ docker-compose.yml | 13 +++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 18ab0c07d..ec0c81834 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -41,7 +41,8 @@ services: networks: - app depends_on: - - nginx + nginx: + condition: service_healthy volumes: - .:/app - /tmp/.X11-unix:/tmp/.X11-unix diff --git a/docker-compose.server.yml b/docker-compose.server.yml index b261c9dd5..fecabd846 100644 --- a/docker-compose.server.yml +++ b/docker-compose.server.yml @@ -39,6 +39,19 @@ services: NGINX_PORT: 8080 NGINX_MAX_BODY_SIZE: 5M NGINX_SET_REAL_IP_FROM: 172.16.0.0/12 + healthcheck: + # $$NGINX_PORT escapes compose-time interpolation; the variable is + # expanded by the shell inside the container against the service's + # environment. + test: + [ + "CMD-SHELL", + 'wget --quiet --tries=1 --spider "http://localhost:$$NGINX_PORT/health"', + ] + interval: 5s + timeout: 3s + retries: 6 + start_period: 5s labels: - "traefik.enable=true" - "traefik.docker.network=frontend" diff --git a/docker-compose.yml b/docker-compose.yml index be33f4ecc..1080b6853 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -67,6 +67,19 @@ services: NGINX_PORT: 8080 NGINX_MAX_BODY_SIZE: 5M NGINX_SET_REAL_IP_FROM: 172.16.0.0/12 + healthcheck: + # $$NGINX_PORT escapes compose-time interpolation; the variable is + # expanded by the shell inside the container against the service's + # environment. + test: + [ + "CMD-SHELL", + 'wget --quiet --tries=1 --spider "http://localhost:$$NGINX_PORT/health"', + ] + interval: 5s + timeout: 3s + retries: 6 + start_period: 5s labels: - "traefik.enable=true" - "traefik.docker.network=frontend" From fcd13f17965b3861f5606e4d7dac054af8c27191 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Wed, 29 Apr 2026 12:38:42 +0200 Subject: [PATCH 28/59] 5402: Restore docker-compose-based PHP CI workflows The merge of release/3.0.0 inadvertently brought back the shivammathur/setup-php matrix setup that was replaced with a docker compose flow earlier on this branch. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/apispec.yaml | 32 ++++---------- .github/workflows/composer_install.yaml | 36 ++++------------ .github/workflows/doctrine.yaml | 51 +++++------------------ .github/workflows/phpunit.yaml | 55 +++++-------------------- .github/workflows/psalm.yaml | 32 ++++---------- .github/workflows/rector.yaml | 32 ++++---------- 6 files changed, 53 insertions(+), 185 deletions(-) diff --git a/.github/workflows/apispec.yaml b/.github/workflows/apispec.yaml index acee4a075..152f5b23c 100644 --- a/.github/workflows/apispec.yaml +++ b/.github/workflows/apispec.yaml @@ -2,49 +2,33 @@ on: pull_request name: Api Spec +env: + COMPOSE_USER: runner + jobs: apispec: runs-on: ubuntu-latest name: API Specification validation - strategy: - fail-fast: false - matrix: - php: ["8.4"] steps: - name: Checkout uses: actions/checkout@v6 with: fetch-depth: 2 - - name: Setup PHP, with composer and extensions - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php}} - extensions: apcu, ctype, iconv, imagick, json, redis, soap, xmlreader, zip - coverage: none - - - name: Get composer cache directory - id: composer-cache - run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - - - name: Cache composer dependencies - uses: actions/cache@v4 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ matrix.php }}-composer- + - name: Setup network + run: docker network create frontend - name: Install Dependencies - run: composer install -q --no-ansi --no-interaction --no-scripts --no-suggest --no-progress --prefer-dist + run: docker compose run --rm phpfpm composer install - name: Export specifications (yaml) - run: bin/console api:openapi:export --yaml --output=public/api-spec-v2.yaml --no-interaction + run: docker compose run --rm phpfpm bin/console api:openapi:export --yaml --output=public/api-spec-v2.yaml --no-interaction - name: Check for changes in specifications (yaml) run: git diff --diff-filter=ACMRT --exit-code public/api-spec-v2.yaml - name: Export specifications (json) - run: bin/console api:openapi:export --output=public/api-spec-v2.json --no-interaction + run: docker compose run --rm phpfpm bin/console api:openapi:export --output=public/api-spec-v2.json --no-interaction - name: Check for changes in specifications (json) run: git diff --diff-filter=ACMRT --exit-code public/api-spec-v2.json diff --git a/.github/workflows/composer_install.yaml b/.github/workflows/composer_install.yaml index 1149a644b..bdce83d6f 100644 --- a/.github/workflows/composer_install.yaml +++ b/.github/workflows/composer_install.yaml @@ -2,39 +2,19 @@ on: pull_request name: Composer install +env: + COMPOSE_USER: runner + jobs: test-composer-install: runs-on: ubuntu-latest - env: - COMPOSER_ALLOW_SUPERUSER: 1 - strategy: - fail-fast: false - matrix: - php: ["8.4"] - name: Composer install in prod mode (PHP ${{ matrix.php}}) + name: Composer install in prod mode steps: - name: Checkout uses: actions/checkout@v6 - - name: Setup PHP, with composer and extensions - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php}} - extensions: apcu, ctype, iconv, imagick, json, redis, soap, xmlreader, zip - coverage: none - - - name: Get composer cache directory - id: composer-cache - run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - - - name: Cache composer dependencies - uses: actions/cache@v4 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ matrix.php }}-composer- + - name: Setup network + run: docker network create frontend - - name: "[prod] Composer install with exported .env variables" - run: | - set -a && source .env && set +a - APP_ENV=prod composer install --no-dev -o + - name: "[prod] Composer install" + run: docker compose run --rm -e APP_ENV=prod phpfpm composer install --no-dev -o diff --git a/.github/workflows/doctrine.yaml b/.github/workflows/doctrine.yaml index ed8b6b3d4..dccba03b8 100644 --- a/.github/workflows/doctrine.yaml +++ b/.github/workflows/doctrine.yaml @@ -2,56 +2,25 @@ on: pull_request name: Doctrine +env: + COMPOSE_USER: runner + jobs: validate-doctrine-shema: runs-on: ubuntu-latest - env: - DATABASE_URL: mysql://db:db@127.0.0.1:3306/db?serverVersion=mariadb-10.5.13 - strategy: - fail-fast: false - matrix: - php: ["8.4"] - name: Validate Schema (PHP ${{ matrix.php}}) - services: - mariadb: - image: mariadb:10.5.13 - env: - MYSQL_USER: db - MYSQL_PASSWORD: db - MYSQL_DATABASE: db - MYSQL_ROOT_PASSWORD: db - ports: - - 3306:3306 - options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + name: Validate Schema steps: - name: Checkout uses: actions/checkout@v6 - - name: Setup PHP, with composer and extensions - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php}} - extensions: apcu, ctype, iconv, imagick, json, redis, soap, xmlreader, zip - coverage: none - - - name: Get composer cache directory - id: composer-cache - run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - - - name: Cache composer dependencies - uses: actions/cache@v4 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ matrix.php }}-composer- + - name: Setup network + run: docker network create frontend - - name: "Composer install with exported .env variables" - run: | - set -a && source .env && set +a - APP_ENV=prod composer install --no-dev -o + - name: "[prod] Composer install" + run: docker compose run --rm -e APP_ENV=prod phpfpm composer install --no-dev -o - name: Run Doctrine Migrations - run: APP_ENV=prod php bin/console doctrine:migrations:migrate --no-interaction + run: docker compose run --rm -e APP_ENV=prod phpfpm bin/console doctrine:migrations:migrate --no-interaction - name: Validate Doctrine schema - run: APP_ENV=prod php bin/console doctrine:schema:validate + run: docker compose run --rm -e APP_ENV=prod phpfpm bin/console doctrine:schema:validate diff --git a/.github/workflows/phpunit.yaml b/.github/workflows/phpunit.yaml index 9b5965fbe..9daee407b 100644 --- a/.github/workflows/phpunit.yaml +++ b/.github/workflows/phpunit.yaml @@ -2,58 +2,25 @@ on: pull_request name: Test +env: + COMPOSE_USER: runner + jobs: phpunit: runs-on: ubuntu-latest - services: - mariadb: - image: mariadb:lts - ports: - - 3306 - env: - MYSQL_USER: db - MYSQL_PASSWORD: db - MYSQL_DATABASE: db_test - MYSQL_ROOT_PASSWORD: password - # https://mariadb.org/mariadb-server-docker-official-images-healthcheck-without-mysqladmin/ - options: >- - --health-cmd="healthcheck.sh --connect --innodb_initialized" - --health-interval=5s - --health-timeout=2s - --health-retries=3 - strategy: - fail-fast: false - matrix: - php: ["8.4"] - name: PHP Unit tests (PHP ${{ matrix.php }}) + name: PHP Unit tests steps: - name: Checkout uses: actions/checkout@v6 - - name: Setup PHP, with composer and extensions - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php}} - extensions: apcu, ctype, iconv, imagick, json, redis, soap, xmlreader, zip - coverage: none - - - name: Get composer cache directory - id: composer-cache - run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - - - name: Cache composer dependencies - uses: actions/cache@v4 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ matrix.php }}-composer- + - name: Setup network + run: docker network create frontend - name: Install Dependencies - run: composer install -q --no-ansi --no-interaction --no-scripts --no-suggest --no-progress --prefer-dist + run: docker compose run --rm phpfpm composer install - name: PHP Unit - Test setup - env: - PORT: ${{ job.services.mariadb.ports[3306] }} - run: | - DATABASE_URL="mysql://db:db@127.0.0.1:$PORT/db_test" composer run test-setup - DATABASE_URL="mysql://db:db@127.0.0.1:$PORT/db_test" composer run test + run: docker compose run --rm phpfpm composer run test-setup + + - name: PHP Unit - Run tests + run: docker compose run --rm phpfpm composer run test diff --git a/.github/workflows/psalm.yaml b/.github/workflows/psalm.yaml index 955f99fe8..4179eae48 100644 --- a/.github/workflows/psalm.yaml +++ b/.github/workflows/psalm.yaml @@ -2,38 +2,22 @@ on: pull_request name: Psalm +env: + COMPOSE_USER: runner + jobs: psalm: runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - php: ["8.4"] - name: Psalm (PHP ${{ matrix.php }}) + name: Psalm steps: - name: Checkout uses: actions/checkout@v6 - - name: Setup PHP, with composer and extensions - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php}} - extensions: apcu, ctype, iconv, imagick, json, redis, soap, xmlreader, zip - coverage: none - - - name: Get composer cache directory - id: composer-cache - run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - - - name: Cache composer dependencies - uses: actions/cache@v4 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ matrix.php }}-composer- + - name: Setup network + run: docker network create frontend - name: Install Dependencies - run: composer install -q --no-ansi --no-interaction --no-scripts --no-suggest --no-progress --prefer-dist + run: docker compose run --rm phpfpm composer install - name: Psalm - run: phpdbg -qrr ./vendor/bin/psalm + run: docker compose run --rm phpfpm vendor/bin/psalm diff --git a/.github/workflows/rector.yaml b/.github/workflows/rector.yaml index 9a98ffd15..47bacbc71 100644 --- a/.github/workflows/rector.yaml +++ b/.github/workflows/rector.yaml @@ -2,38 +2,22 @@ on: pull_request name: Rector +env: + COMPOSE_USER: runner + jobs: rector: runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - php: ["8.4"] - name: Rector (PHP ${{ matrix.php }}) + name: Rector steps: - name: Checkout uses: actions/checkout@v6 - - name: Setup PHP, with composer and extensions - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php}} - extensions: apcu, ctype, iconv, imagick, json, redis, soap, xmlreader, zip - coverage: none - - - name: Get composer cache directory - id: composer-cache - run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - - - name: Cache composer dependencies - uses: actions/cache@v4 - with: - path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }} - restore-keys: ${{ matrix.php }}-composer- + - name: Setup network + run: docker network create frontend - name: Install Dependencies - run: composer install -q --no-ansi --no-interaction --no-scripts --no-suggest --no-progress --prefer-dist + run: docker compose run --rm phpfpm composer install - name: Rector - run: phpdbg -qrr ./vendor/bin/rector --dry-run + run: docker compose run --rm phpfpm vendor/bin/rector --dry-run From 4c4973450fe30361474cccd3238b0523a8048f24 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Wed, 29 Apr 2026 13:03:38 +0200 Subject: [PATCH 29/59] 5402: Generate release.json on image build and release tarball Mirrors itk-dev/os2display-api-service#2. Inlines the .github/Taskfile into github_build_release.yml and aligns its cleanup with .dockerignore. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/Taskfile.yml | 46 ------------- .github/workflows/build-images.yml | 13 ++-- .github/workflows/github_build_release.yml | 67 +++++++++++++++++-- CHANGELOG.md | 3 + infrastructure/build-n-push.sh | 5 +- infrastructure/display-api-service/Dockerfile | 10 +++ infrastructure/nginx/Dockerfile | 10 +++ 7 files changed, 98 insertions(+), 56 deletions(-) delete mode 100644 .github/Taskfile.yml diff --git a/.github/Taskfile.yml b/.github/Taskfile.yml deleted file mode 100644 index 2cb169d48..000000000 --- a/.github/Taskfile.yml +++ /dev/null @@ -1,46 +0,0 @@ -# Task file for GitHub Actions, https://taskfile.dev/ - -version: "3" - -# https://taskfile.dev/usage/#env-files -dotenv: [".env.local", ".env"] - -tasks: - default: - desc: "List all tasks" - cmds: - - task --list-all - silent: true - - build-prod: - desc: "Build application for production" - cmds: - - task setup-network - - task composer-install - - task npm-install - - task install-cleanup - - setup-network: - desc: "Setup docker frontend network" - cmds: - - docker network create frontend - - composer-install: - desc: "Install dependencies with composer." - cmds: - - docker compose run --rm --env APP_ENV=prod phpfpm composer install --no-dev -o --classmap-authoritative - - docker compose run --rm --env APP_ENV=prod phpfpm composer clear-cache - - npm-install: - desc: "Installs node dependencies with npm." - cmds: - - docker compose run --rm node npm install - - docker compose run --rm node npm run build - - install-cleanup: - desc: "Cleanup after install" - cmds: - - rm -rf infrastructure - - rm -rf fixtures - - rm -rf tests - - rm -rf node_modules diff --git a/.github/workflows/build-images.yml b/.github/workflows/build-images.yml index 91bdc9aca..3b99f142f 100644 --- a/.github/workflows/build-images.yml +++ b/.github/workflows/build-images.yml @@ -30,6 +30,11 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Set release timestamp + run: | + echo "APP_RELEASE_TIMESTAMP=$(date +%s)" >> "$GITHUB_ENV" + echo "APP_RELEASE_TIME=$(date -u)" >> "$GITHUB_ENV" + - name: Docker meta (API) id: meta-api uses: docker/metadata-action@v6 @@ -61,8 +66,8 @@ jobs: # '3.0.0-RC1') without bespoke string handling per trigger. build-args: | APP_VERSION=${{ steps.meta-api.outputs.version }} - APP_RELEASE_VERSION=${{ steps.meta-api.outputs.version }} - APP_RELEASE_TIMESTAMP=${{ github.run_number }} + APP_RELEASE_TIMESTAMP=${{ env.APP_RELEASE_TIMESTAMP }} + APP_RELEASE_TIME=${{ env.APP_RELEASE_TIME }} push: true tags: ${{ steps.meta-api.outputs.tags }} labels: ${{ steps.meta-api.outputs.labels }} @@ -101,8 +106,8 @@ jobs: repository-root=. build-args: | APP_VERSION=${{ steps.meta-nginx.outputs.version }} - APP_RELEASE_VERSION=${{ steps.meta-nginx.outputs.version }} - APP_RELEASE_TIMESTAMP=${{ github.run_number }} + APP_RELEASE_TIMESTAMP=${{ env.APP_RELEASE_TIMESTAMP }} + APP_RELEASE_TIME=${{ env.APP_RELEASE_TIME }} push: true tags: ${{ steps.meta-nginx.outputs.tags }} labels: ${{ steps.meta-nginx.outputs.labels }} diff --git a/.github/workflows/github_build_release.yml b/.github/workflows/github_build_release.yml index 482cbdc23..210bf1b37 100644 --- a/.github/workflows/github_build_release.yml +++ b/.github/workflows/github_build_release.yml @@ -18,12 +18,58 @@ jobs: - name: Checkout uses: actions/checkout@v6 - - name: Install Task task runner - uses: go-task/setup-task@v2 + - name: Setup network + run: docker network create frontend - - name: Install, Build, Cleanup + - name: Composer install (prod) run: | - task --taskfile=.github/Taskfile.yaml build-prod + docker compose run --rm --env APP_ENV=prod phpfpm \ + composer install --no-dev -o --classmap-authoritative + docker compose run --rm --env APP_ENV=prod phpfpm composer clear-cache + + - name: NPM install and build + run: | + docker compose run --rm node npm install + docker compose run --rm node npm run build + + - name: Cleanup before packaging + run: | + # Mirrors .dockerignore for tarball hygiene, minus vendor/ and + # public/build/ which we just produced and need to ship. + rm -rf \ + infrastructure \ + fixtures \ + tests \ + node_modules \ + docs \ + scripts \ + var \ + test-results \ + playwright-report \ + blob-report \ + playwright \ + public/fixtures + rm -f \ + docker-compose*.yml \ + Taskfile.yml \ + playwright.config.ts \ + phpstan.dist.neon \ + phpunit.xml \ + phpunit.xml.dist \ + psalm.xml \ + psalm-baseline.xml \ + rector.php \ + xdebug.ini \ + launch.json + + - name: Write release.json + run: | + printf '{"releaseTimestamp": %s, "releaseTime": "%s", "releaseVersion": "%s"}\n' \ + "$(date +%s)" \ + "$(date -u)" \ + "${{ github.ref_name }}" \ + > public/release.json + cat public/release.json - name: Make assets dir run: | @@ -37,6 +83,19 @@ jobs: - name: Create checksum run: sha256sum ../assets/${{ github.event.repository.name }}-${{ github.ref_name }}.tar.gz > ../assets/checksum.txt + # Mirror docker/metadata-action's 'latest=auto' rule used by build-images.yml: + # any semver tag with a pre-release identifier ('3.0.0-RC1', '3.0.0-beta') + # is published as a GitHub pre-release; final tags ('3.0.0') are not. + - name: Detect pre-release + id: prerelease + shell: bash + run: | + if [[ "$GITHUB_REF_NAME" == *-* ]]; then + echo "flag=--prerelease" >> "$GITHUB_OUTPUT" + else + echo "flag=" >> "$GITHUB_OUTPUT" + fi + - name: Create a release in GitHub and uploads assets run: | gh release create ${{ github.ref_name }} --verify-tag --generate-notes ${{ steps.prerelease.outputs.flag }} ../assets/*.* diff --git a/CHANGELOG.md b/CHANGELOG.md index 66b714a3d..8cc13c83a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,9 @@ All notable changes to this project will be documented in this file. - Switched image build pipeline to GHCR with multi-arch layer caching. - Aligned the nginx image env-var contract: split `NGINX_FPM_SERVICE` and `NGINX_FPM_PORT`, raised upload cap and trusted-proxy CIDR defaults. +- Image build now writes `public/release.json` so the client's + release-loader can fetch it. The same file is included in the GitHub + Release tarball. ### NB! Prior to 3.x the project was split into separate repositories diff --git a/infrastructure/build-n-push.sh b/infrastructure/build-n-push.sh index 0f64eee74..2a0fc645d 100755 --- a/infrastructure/build-n-push.sh +++ b/infrastructure/build-n-push.sh @@ -4,6 +4,7 @@ set -eux APP_VERSION="${APP_VERSION:-develop}" REGISTRY="${REGISTRY:-ghcr.io/os2display}" RELEASE_TIMESTAMP="$(date +%s)" +RELEASE_TIME="$(date -u)" # Set BUILD_LOAD=1 to build for the host platform and load into the local # docker daemon instead of pushing. Useful for local smoke tests; production @@ -25,8 +26,8 @@ docker buildx build \ --pull ${OUTPUT} \ --build-context repository-root=. \ --build-arg APP_VERSION="${APP_VERSION}" \ - --build-arg APP_RELEASE_VERSION="${APP_VERSION}" \ --build-arg APP_RELEASE_TIMESTAMP="${RELEASE_TIMESTAMP}" \ + --build-arg APP_RELEASE_TIME="${RELEASE_TIME}" \ --tag "${REGISTRY}/display-api-service:${APP_VERSION}" \ --file infrastructure/display-api-service/Dockerfile \ infrastructure/display-api-service @@ -38,8 +39,8 @@ docker buildx build \ --pull ${OUTPUT} \ --build-context repository-root=. \ --build-arg APP_VERSION="${APP_VERSION}" \ - --build-arg APP_RELEASE_VERSION="${APP_VERSION}" \ --build-arg APP_RELEASE_TIMESTAMP="${RELEASE_TIMESTAMP}" \ + --build-arg APP_RELEASE_TIME="${RELEASE_TIME}" \ --tag "${REGISTRY}/display-api-service-nginx:${APP_VERSION}" \ --file infrastructure/nginx/Dockerfile \ infrastructure/nginx diff --git a/infrastructure/display-api-service/Dockerfile b/infrastructure/display-api-service/Dockerfile index be3e5b4b3..762bae1e4 100644 --- a/infrastructure/display-api-service/Dockerfile +++ b/infrastructure/display-api-service/Dockerfile @@ -28,6 +28,8 @@ FROM itkdev/php8.4-fpm:alpine AS api_app_builder LABEL maintainer="ITK Dev " ARG APP_VERSION="develop" +ARG APP_RELEASE_TIMESTAMP=0 +ARG APP_RELEASE_TIME="" ENV APP_CLIENT_PATH=/app \ APP_API_PATH=/var/www/html @@ -57,6 +59,14 @@ COPY --chown=deploy:deploy --from=client_app_builder ${APP_CLIENT_PATH}/public/b # Re-run composer install after application code copied to image to complete install RUN APP_ENV=prod composer install --no-dev --optimize-autoloader --classmap-authoritative +# Write release.json into the public root so the client (assets/shared/release-loader.js) +# can fetch it at /release.json. Shape matches docs/release-example.json. +RUN printf '{"releaseTimestamp": %s, "releaseTime": "%s", "releaseVersion": "%s"}\n' \ + "${APP_RELEASE_TIMESTAMP}" \ + "${APP_RELEASE_TIME}" \ + "${APP_VERSION}" \ + > public/release.json + ######## PHP-FPM (API) production image ######## FROM itkdev/php8.4-fpm:alpine diff --git a/infrastructure/nginx/Dockerfile b/infrastructure/nginx/Dockerfile index 92cabe375..3a76f857d 100644 --- a/infrastructure/nginx/Dockerfile +++ b/infrastructure/nginx/Dockerfile @@ -27,6 +27,8 @@ FROM itkdev/php8.4-fpm:alpine AS api_app_builder LABEL maintainer="ITK Dev " ARG APP_VERSION="develop" +ARG APP_RELEASE_TIMESTAMP=0 +ARG APP_RELEASE_TIME="" ENV APP_CLIENT_PATH=/app \ APP_API_PATH=/var/www/html @@ -56,6 +58,14 @@ COPY --chown=deploy:deploy --from=client_app_builder ${APP_CLIENT_PATH}/public/b # Re-run composer install after application code copied to image to complete install RUN APP_ENV=prod composer install --no-dev --optimize-autoloader --classmap-authoritative +# Write release.json into the public root so the client (assets/shared/release-loader.js) +# can fetch it at /release.json. Shape matches docs/release-example.json. +RUN printf '{"releaseTimestamp": %s, "releaseTime": "%s", "releaseVersion": "%s"}\n' \ + "${APP_RELEASE_TIMESTAMP}" \ + "${APP_RELEASE_TIME}" \ + "${APP_VERSION}" \ + > public/release.json + ######## Nginx production image ######## From 8624b802d9ab3baeade22ad23bd0a33913a9adab Mon Sep 17 00:00:00 2001 From: turegjorup Date: Thu, 30 Apr 2026 10:49:35 +0200 Subject: [PATCH 30/59] 390: Allow same-origin iframe embedding for admin previews The admin embeds the client via iframe to render screen/playlist previews and the fullscreen slide view. With X-Frame-Options: DENY, the browser refused to render the iframe. Switch to frame-ancestors 'self' (CSP Level 2+, the modern control) and SAMEORIGIN as defense-in-depth for older browsers. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 2 ++ infrastructure/nginx/etc/templates/default.conf.template | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cc13c83a..37c4491b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,8 @@ All notable changes to this project will be documented in this file. - Switched image build pipeline to GHCR with multi-arch layer caching. - Aligned the nginx image env-var contract: split `NGINX_FPM_SERVICE` and `NGINX_FPM_PORT`, raised upload cap and trusted-proxy CIDR defaults. +- Allowed same-origin iframe embedding so the admin's screen/playlist + preview and fullscreen slide view work (#390). - Image build now writes `public/release.json` so the client's release-loader can fetch it. The same file is included in the GitHub Release tarball. diff --git a/infrastructure/nginx/etc/templates/default.conf.template b/infrastructure/nginx/etc/templates/default.conf.template index 2710ad922..5baa1b9ae 100644 --- a/infrastructure/nginx/etc/templates/default.conf.template +++ b/infrastructure/nginx/etc/templates/default.conf.template @@ -23,7 +23,10 @@ server { # Security headers add_header X-Content-Type-Options "nosniff" always; - add_header X-Frame-Options "DENY" always; + # frame-ancestors is the modern primary control; X-Frame-Options is + # defense-in-depth for pre-CSP-2 browsers (#390). + add_header Content-Security-Policy "frame-ancestors 'self'" always; + add_header X-Frame-Options "SAMEORIGIN" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; # Real IP from reverse proxy From cc33ddd49cadd34f8ec413bf09ea3b9235ba541e Mon Sep 17 00:00:00 2001 From: turegjorup Date: Thu, 30 Apr 2026 13:47:53 +0200 Subject: [PATCH 31/59] Add .git-blame-ignore-revs to skip bulk style commit Skip the coding-standards reformatting commit (6a91050b) so `git blame` points at the substantive author of each line. Enable locally with: git config blame.ignoreRevsFile .git-blame-ignore-revs GitHub's blame view honors this file automatically. Co-Authored-By: Claude Opus 4.7 (1M context) --- .git-blame-ignore-revs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .git-blame-ignore-revs diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 000000000..af4e6c73d --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,9 @@ +# Revisions to skip when running `git blame`. +# +# Configure once locally: +# git config blame.ignoreRevsFile .git-blame-ignore-revs +# +# Each line is a full commit SHA followed by a comment. + +# 7329: Applied coding standards (bulk style commit during psalm→phpstan migration) +6a91050b2339fe7c1b5080f9cdb3219fe1adc739 From abd12dfcf20c341070b06840c227f0cbbb10abef Mon Sep 17 00:00:00 2001 From: turegjorup Date: Thu, 30 Apr 2026 14:17:28 +0200 Subject: [PATCH 32/59] Drop set -x and dead composer1 branch from entrypoint set -x echoes every executed command to stderr at every container start in production, which is verbose and risks leaking expanded values into logs. The COMPOSER_VERSION=1 branch symlinked /usr/bin/composer1, but the image only ships composer 2 (copied from composer:2 to /usr/local/bin/composer), so the conditional was dead code that would have produced a broken symlink without an immediate error. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../display-api-service/docker-entrypoint.sh | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/infrastructure/display-api-service/docker-entrypoint.sh b/infrastructure/display-api-service/docker-entrypoint.sh index 744897e8b..111d39a23 100644 --- a/infrastructure/display-api-service/docker-entrypoint.sh +++ b/infrastructure/display-api-service/docker-entrypoint.sh @@ -1,6 +1,6 @@ #!/bin/sh -set -eux +set -eu ## Dump dotenv files into PHP for better performance. ## @see https://symfony.com/doc/6.4/configuration.html#configuring-environment-variables-in-production @@ -9,15 +9,4 @@ composer dump-env prod ## Warm-up Symfony cache (with the current configuration). /var/www/html/bin/console --env=prod cache:warmup -## Set selected composer version. Default version 2. -if [ ! -z "${COMPOSER_VERSION}" ]; then - if [ "${COMPOSER_VERSION}" = "1" ]; then - ln -fs /usr/bin/composer1 /home/deploy/bin/composer - else - ln -fs /usr/bin/composer2 /home/deploy/bin/composer - fi -else - ln -fs /usr/bin/composer2 /home/deploy/bin/composer -fi - exec php-fpm "$@" From 11842df1015490350c63d8b612595d30054fabe0 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Thu, 30 Apr 2026 14:17:48 +0200 Subject: [PATCH 33/59] Switch API image to ENTRYPOINT/CMD pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CMD ["docker-entrypoint.sh"] is replaced by the entire CMD whenever an operator passes a command at run time (e.g. docker run --rm bin/console …), which silently skipped the dump-env + cache:warmup preamble. ENTRYPOINT keeps the preamble running for every command and the existing exec "$@" tail in the script forwards CMD verbatim, so the default php-fpm invocation is unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) --- infrastructure/display-api-service/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/infrastructure/display-api-service/Dockerfile b/infrastructure/display-api-service/Dockerfile index 762bae1e4..3c7531291 100644 --- a/infrastructure/display-api-service/Dockerfile +++ b/infrastructure/display-api-service/Dockerfile @@ -111,4 +111,5 @@ RUN mkdir -p ./config/secrets WORKDIR ${APP_API_PATH} -CMD [ "docker-entrypoint.sh" ] +ENTRYPOINT ["docker-entrypoint.sh"] +CMD ["php-fpm"] From 34f065b4c5f454ca6007bfbeeee83836ae48891a Mon Sep 17 00:00:00 2001 From: turegjorup Date: Thu, 30 Apr 2026 14:18:27 +0200 Subject: [PATCH 34/59] Collapse the API image ENV block, drop unused COMPOSER_VERSION Blank lines between trailing-backslash continuations are not portable in Dockerfiles; the classic builder terminates the ENV at the blank line, leaving the PHP_OPCACHE_* / PHP_PM_* tuning either silently unset or as malformed instructions. Joining the block into one continuous declaration removes that risk. COMPOSER_VERSION was only read by the now-removed composer1 branch of the entrypoint, so it is dropped at the same time. Co-Authored-By: Claude Opus 4.7 (1M context) --- infrastructure/display-api-service/Dockerfile | 5 ----- 1 file changed, 5 deletions(-) diff --git a/infrastructure/display-api-service/Dockerfile b/infrastructure/display-api-service/Dockerfile index 3c7531291..8bd4a6ef1 100644 --- a/infrastructure/display-api-service/Dockerfile +++ b/infrastructure/display-api-service/Dockerfile @@ -75,15 +75,10 @@ LABEL maintainer="ITK Dev " ENV APP_CLIENT_PATH=/app \ APP_API_PATH=/var/www/html \ APP_ENV=prod \ - COMPOSER_VERSION=2 \ - - # OpCache PHP_OPCACHE_ENABLED=1 \ PHP_OPCACHE_VALIDATE_TIMESTAMPS=0 \ PHP_OPCACHE_MAX_ACCELERATED_FILES=20000 \ PHP_OPCACHE_MEMORY_CONSUMPTION=256 \ - - # FPM pool PHP_PM_TYPE="dynamic" \ PHP_PM_MAX_CHILDREN="24" \ PHP_PM_MAX_REQUESTS="0" \ From 0e5e2049ee3b258c5643075a6759ce301de39364 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Thu, 30 Apr 2026 14:19:09 +0200 Subject: [PATCH 35/59] Drop unused APP_RELEASE_VERSION build arg Declared in three stages across the API and Nginx Dockerfiles but never referenced; the release.json write uses APP_VERSION. Removing the dead arg keeps the build-arg surface honest and avoids implying a contract that doesn't exist. Co-Authored-By: Claude Opus 4.7 (1M context) --- infrastructure/display-api-service/Dockerfile | 1 - infrastructure/nginx/Dockerfile | 2 -- 2 files changed, 3 deletions(-) diff --git a/infrastructure/display-api-service/Dockerfile b/infrastructure/display-api-service/Dockerfile index 8bd4a6ef1..5491df3fa 100644 --- a/infrastructure/display-api-service/Dockerfile +++ b/infrastructure/display-api-service/Dockerfile @@ -3,7 +3,6 @@ FROM node:24-alpine AS client_app_builder LABEL maintainer="ITK Dev " ARG APP_VERSION="develop" -ARG APP_RELEASE_VERSION="develop" ARG APP_RELEASE_TIMESTAMP=0 ENV APP_CLIENT_PATH=/app diff --git a/infrastructure/nginx/Dockerfile b/infrastructure/nginx/Dockerfile index 3a76f857d..1af59e2d0 100644 --- a/infrastructure/nginx/Dockerfile +++ b/infrastructure/nginx/Dockerfile @@ -3,7 +3,6 @@ FROM node:24-alpine AS client_app_builder LABEL maintainer="ITK Dev " ARG APP_VERSION="develop" -ARG APP_RELEASE_VERSION="develop" ARG APP_RELEASE_TIMESTAMP=0 ENV APP_CLIENT_PATH=/app @@ -73,7 +72,6 @@ FROM nginxinc/nginx-unprivileged:alpine LABEL maintainer="ITK Dev " ARG APP_VERSION="develop" -ARG APP_RELEASE_VERSION="develop" ARG APP_RELEASE_TIMESTAMP=0 ARG UID=101 ARG GID=101 From 0fad57a253e31c49ba846544b453c8ee3f3a66b2 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Thu, 30 Apr 2026 14:21:19 +0200 Subject: [PATCH 36/59] Match /health exactly in the nginx template The prefix-form location /health also matched /health/anything and /healthcheck, exposing healthy responses on URIs that aren't the intended probe target. Switching to an exact match (location = /health) confines the 200 response to the single canonical path the container HEALTHCHECK and external probes use. Co-Authored-By: Claude Opus 4.7 (1M context) --- infrastructure/nginx/etc/templates/default.conf.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure/nginx/etc/templates/default.conf.template b/infrastructure/nginx/etc/templates/default.conf.template index 5baa1b9ae..4b574d402 100644 --- a/infrastructure/nginx/etc/templates/default.conf.template +++ b/infrastructure/nginx/etc/templates/default.conf.template @@ -12,7 +12,7 @@ server { index index.php index.html; # Health check endpoint - location /health { + location = /health { access_log off; add_header Content-Type text/plain; return 200 "healthy\n"; From 063ad56fe26b7215fa7b4506a3962730b3ea201d Mon Sep 17 00:00:00 2001 From: turegjorup Date: Thu, 30 Apr 2026 14:21:42 +0200 Subject: [PATCH 37/59] Fix typo and stale registry reference in infrastructure README MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'devolp' → 'develop'; the Docker Hub section is replaced with the GHCR location now that the build pipeline pushes to ghcr.io. Adds the trailing newline so the file no longer reads as truncated. Co-Authored-By: Claude Opus 4.7 (1M context) --- infrastructure/Readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/infrastructure/Readme.md b/infrastructure/Readme.md index 3d60839e9..bc7de79c1 100644 --- a/infrastructure/Readme.md +++ b/infrastructure/Readme.md @@ -9,12 +9,12 @@ Two images are built: ## Github Actions -Both images are built automatically on push to the `devolp` branch, and on tag creation. They are tagged with the +Both images are built automatically on push to the `develop` branch, and on tag creation. They are tagged with the same version as the application. -## Docker Hub +## GitHub Container Registry -Images are published to +Images are published to . ## Building images locally From ac78a494c45c4919a45f416d734bfd6395fbe756 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Thu, 30 Apr 2026 14:23:20 +0200 Subject: [PATCH 38/59] Layer nginx image on the published API image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The nginx Dockerfile previously duplicated the client_app_builder and api_app_builder stages from the API Dockerfile, then copied public/ out of the duplicate. With CI cache scopes split per image, every build re-ran npm ci and composer install for both images, doubling build time on amd64 and arm64. The duplicated release.json write was also a second source of truth for the same data. Replace those 67 duplicated lines with a small FROM stage that pulls the API image just published in the previous workflow step. Multi-arch manifests resolve per platform, so each arch copies its own public/ tree without QEMU re-execution. The build context for the nginx step shrinks to infrastructure/nginx/ — repository-root and the APP_RELEASE_* build args are no longer needed there. Local build-n-push.sh keeps its API-then-nginx order; with BUILD_LOAD=1 the API tag is loaded into the local daemon and BuildKit reuses it without a network pull. The README is updated to describe the new relationship instead of the old shared-stage rationale. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/build-images.yml | 10 ++-- infrastructure/Readme.md | 14 +++-- infrastructure/build-n-push.sh | 9 ++-- infrastructure/nginx/Dockerfile | 83 ++++-------------------------- 4 files changed, 29 insertions(+), 87 deletions(-) diff --git a/.github/workflows/build-images.yml b/.github/workflows/build-images.yml index 3b99f142f..cd45bc912 100644 --- a/.github/workflows/build-images.yml +++ b/.github/workflows/build-images.yml @@ -102,12 +102,12 @@ jobs: with: context: ./infrastructure/nginx/ file: ./infrastructure/nginx/Dockerfile - build-contexts: | - repository-root=. + # Reuse the API image's resolved version so the Nginx FROM + # always tracks the tag that was just pushed in the previous + # step, even if the two metadata-action invocations ever + # diverge. build-args: | - APP_VERSION=${{ steps.meta-nginx.outputs.version }} - APP_RELEASE_TIMESTAMP=${{ env.APP_RELEASE_TIMESTAMP }} - APP_RELEASE_TIME=${{ env.APP_RELEASE_TIME }} + APP_VERSION=${{ steps.meta-api.outputs.version }} push: true tags: ${{ steps.meta-nginx.outputs.tags }} labels: ${{ steps.meta-nginx.outputs.labels }} diff --git a/infrastructure/Readme.md b/infrastructure/Readme.md index bc7de79c1..4daf6b1ad 100644 --- a/infrastructure/Readme.md +++ b/infrastructure/Readme.md @@ -22,8 +22,12 @@ A script is provided to build the images locally: `build-n-push.sh` ## Build process -Both images uses multistage builds, with the first two stages being identical. And the final stage is optimized for -the specific image. - -This is done because both images requires files from both the `npm` and `composer` build stages. And while having a -shared build stage when building locally is possible, it's not feasible when building on Github Actions. +The API (php-fpm) image is built first via a multistage build that performs the `npm` and `composer` build stages +and bakes the result into `/var/www/html`. The nginx image then layers on top of the published API image as a +`FROM` stage and copies the `public/` tree out of it. There is no duplicated builder logic — the `npm`/`composer` +work runs once per CI run. + +Because the nginx build depends on the API image, the workflow builds and pushes the API image first; the nginx +step pulls the just-published manifest. For local builds, `build-n-push.sh` runs the same order, so when +`BUILD_LOAD=1` the API image is loaded into the local daemon under the same tag and BuildKit reuses it without a +network pull. diff --git a/infrastructure/build-n-push.sh b/infrastructure/build-n-push.sh index 2a0fc645d..21bff9064 100755 --- a/infrastructure/build-n-push.sh +++ b/infrastructure/build-n-push.sh @@ -32,15 +32,16 @@ docker buildx build \ --file infrastructure/display-api-service/Dockerfile \ infrastructure/display-api-service -# Nginx image. Context is the nginx infra dir so 'COPY etc /etc/nginx' resolves. +# Nginx image layers on the just-built API image (single source of truth for +# public/), so it has no builder stages of its own. With --push the FROM +# resolves against the registry; with --load the API tag is in the local +# daemon and BuildKit reuses it without a network pull. # shellcheck disable=SC2086 docker buildx build \ ${PLATFORMS} \ --pull ${OUTPUT} \ - --build-context repository-root=. \ --build-arg APP_VERSION="${APP_VERSION}" \ - --build-arg APP_RELEASE_TIMESTAMP="${RELEASE_TIMESTAMP}" \ - --build-arg APP_RELEASE_TIME="${RELEASE_TIME}" \ + --build-arg APP_IMAGE="${REGISTRY}/display-api-service" \ --tag "${REGISTRY}/display-api-service-nginx:${APP_VERSION}" \ --file infrastructure/nginx/Dockerfile \ infrastructure/nginx diff --git a/infrastructure/nginx/Dockerfile b/infrastructure/nginx/Dockerfile index 1af59e2d0..ae2d2d309 100644 --- a/infrastructure/nginx/Dockerfile +++ b/infrastructure/nginx/Dockerfile @@ -1,83 +1,20 @@ -######## Clients [Screen|Admin] build ######## -FROM node:24-alpine AS client_app_builder -LABEL maintainer="ITK Dev " - -ARG APP_VERSION="develop" -ARG APP_RELEASE_TIMESTAMP=0 - -ENV APP_CLIENT_PATH=/app -WORKDIR ${APP_CLIENT_PATH} - -# Copy only necessary files for npm install -COPY --from=repository-root package.json package-lock.json vite.config.js ./ - -# Install dependencies -RUN npm ci --no-audit --no-fund - -# Copy source files needed for build -COPY --from=repository-root assets/ ./assets/ - -# Build the application with version info -RUN npm run build - - -######### API backend build ######## -FROM itkdev/php8.4-fpm:alpine AS api_app_builder -LABEL maintainer="ITK Dev " - ARG APP_VERSION="develop" -ARG APP_RELEASE_TIMESTAMP=0 -ARG APP_RELEASE_TIME="" -ENV APP_CLIENT_PATH=/app \ - APP_API_PATH=/var/www/html - -USER root - -# Add composer in from the official composer image (also alpine). -COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer - -WORKDIR ${APP_API_PATH} - -USER deploy - -# Copy only composer files first for better layer caching -COPY --chown=deploy:deploy --from=repository-root composer.json composer.lock symfony.lock ${APP_API_PATH}/ - -# Pre-install composer packages first (better image layer caching) - application code not present so have to be re-run -RUN APP_ENV=prod composer install --no-dev -o --classmap-authoritative --no-scripts - -# Copy application source (needed for build step) -COPY --chown=deploy:deploy --from=repository-root ./ ${APP_API_PATH}/ - -# Copy javascript build files. This ensures that vite manifest files are availiable when "composer insatll" -# triggers a "cache:clear" enabling the vite bundle to generate cache configuration files -# @see https://symfony-vite.pentatrion.com/guide/performance.html#caching-configuration-files-%F0%9F%8F%83 -COPY --chown=deploy:deploy --from=client_app_builder ${APP_CLIENT_PATH}/public/build ./public/build - -# Re-run composer install after application code copied to image to complete install -RUN APP_ENV=prod composer install --no-dev --optimize-autoloader --classmap-authoritative - -# Write release.json into the public root so the client (assets/shared/release-loader.js) -# can fetch it at /release.json. Shape matches docs/release-example.json. -RUN printf '{"releaseTimestamp": %s, "releaseTime": "%s", "releaseVersion": "%s"}\n' \ - "${APP_RELEASE_TIMESTAMP}" \ - "${APP_RELEASE_TIME}" \ - "${APP_VERSION}" \ - > public/release.json +ARG APP_IMAGE="ghcr.io/os2display/display-api-service" +# Pull the API image that was built and pushed in the preceding step. +# Multi-arch manifests resolve per platform, so the arm64 nginx build +# copies arm64 contents and amd64 copies amd64 — no QEMU re-execution +# of npm/composer for the second image. +FROM ${APP_IMAGE}:${APP_VERSION} AS app ######## Nginx production image ######## FROM nginxinc/nginx-unprivileged:alpine LABEL maintainer="ITK Dev " -ARG APP_VERSION="develop" -ARG APP_RELEASE_TIMESTAMP=0 ARG UID=101 -ARG GID=101 -ENV APP_CLIENT_PATH=/app \ - APP_API_PATH=/var/www/html \ +ENV APP_API_PATH=/var/www/html \ NGINX_PORT=8080 \ NGINX_FPM_SERVICE=os2display \ NGINX_FPM_PORT=9000 \ @@ -87,9 +24,9 @@ ENV APP_CLIENT_PATH=/app \ WORKDIR ${APP_API_PATH} -# Copy built client assets with permissions baked in (avoids a duplicate -# layer that a separate `RUN chmod -R` would produce — saves ~19 MB). -COPY --from=api_app_builder --chown=$UID:0 --chmod=775 ${APP_API_PATH}/public ./public +# Single source of truth: public/ (incl. build/ and release.json) comes +# from the just-published API image, not a duplicated builder stage. +COPY --from=app --chown=$UID:0 --chmod=775 ${APP_API_PATH}/public ./public # Copy nginx configuration COPY --chown=$UID:0 etc /etc/nginx From a85f17492f8793ba044085231cb2d2c6dfd1bd00 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Thu, 30 Apr 2026 14:28:23 +0200 Subject: [PATCH 39/59] Skip --pull on nginx build in BUILD_LOAD mode With BUILD_LOAD=1 the API image only exists in the local daemon, so --pull on the nginx step forced BuildKit to ask the registry for it and aborted with a 403 from anonymous GHCR. Conditional --pull keeps the production push path (which legitimately needs the registry copy) unchanged while letting local smoke builds chain through the local daemon. Co-Authored-By: Claude Opus 4.7 (1M context) --- infrastructure/build-n-push.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/infrastructure/build-n-push.sh b/infrastructure/build-n-push.sh index 21bff9064..9f9a9da01 100755 --- a/infrastructure/build-n-push.sh +++ b/infrastructure/build-n-push.sh @@ -12,9 +12,14 @@ RELEASE_TIME="$(date -u)" if [ "${BUILD_LOAD:-0}" = "1" ]; then PLATFORMS="" OUTPUT="--load" + # In load mode the nginx FROM points at the API image we just loaded into + # the local daemon. --pull would force a registry lookup that fails (and + # has no business pulling the just-built image anyway). + NGINX_PULL="" else PLATFORMS="--platform=linux/amd64,linux/arm64" OUTPUT="--push" + NGINX_PULL="--pull" fi # API (php-fpm) image. Context is the API infra dir so the Dockerfile picks @@ -39,7 +44,7 @@ docker buildx build \ # shellcheck disable=SC2086 docker buildx build \ ${PLATFORMS} \ - --pull ${OUTPUT} \ + ${NGINX_PULL} ${OUTPUT} \ --build-arg APP_VERSION="${APP_VERSION}" \ --build-arg APP_IMAGE="${REGISTRY}/display-api-service" \ --tag "${REGISTRY}/display-api-service-nginx:${APP_VERSION}" \ From b0bc3a0f53e550ccd4d282167a902584b57aea94 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Thu, 30 Apr 2026 22:44:41 +0200 Subject: [PATCH 40/59] Forward CMD verbatim from the entrypoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit exec php-fpm "$@" hard-coded php-fpm as the command, so with CMD set to ["php-fpm"] the script ran 'php-fpm php-fpm' (FPM rejected the extra arg and printed help), and 'docker run bin/console …' went to FPM instead of the console. exec "$@" lets the entrypoint be a pure preamble + handoff to whatever CMD names — the default [php-fpm] path starts FPM cleanly, and overriding the command runs that command after the dump-env + cache:warmup preamble. Co-Authored-By: Claude Opus 4.7 (1M context) --- infrastructure/display-api-service/docker-entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure/display-api-service/docker-entrypoint.sh b/infrastructure/display-api-service/docker-entrypoint.sh index 111d39a23..95309a935 100644 --- a/infrastructure/display-api-service/docker-entrypoint.sh +++ b/infrastructure/display-api-service/docker-entrypoint.sh @@ -9,4 +9,4 @@ composer dump-env prod ## Warm-up Symfony cache (with the current configuration). /var/www/html/bin/console --env=prod cache:warmup -exec php-fpm "$@" +exec "$@" From 692087c2f89c97359272b1515306fc85af1f310f Mon Sep 17 00:00:00 2001 From: turegjorup Date: Fri, 1 May 2026 08:12:37 +0200 Subject: [PATCH 41/59] Cache vendor across PHP CI workflows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each PHP workflow currently spends ~30s on a cold composer install for the same composer.lock — across nine workflows that's roughly four minutes of duplicated work per push. actions/cache keyed on composer.lock makes the install a near no-op when the lockfile is unchanged. Dev installs share one key (vendor-php8.4-…); the doctrine workflow's --no-dev install gets its own (vendor-prod-php8.4-…) so prod and dev caches don't clobber each other. composer_install.yaml intentionally stays uncached: its purpose is validating that a fresh prod-mode install works. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/apispec.yaml | 7 +++++++ .github/workflows/code-analysis.yaml | 7 +++++++ .github/workflows/composer.yaml | 6 ++++++ .github/workflows/doctrine.yaml | 7 +++++++ .github/workflows/php.yaml | 6 ++++++ .github/workflows/phpunit.yaml | 7 +++++++ .github/workflows/playwright.yaml | 7 +++++++ .github/workflows/rector.yaml | 7 +++++++ .github/workflows/twig.yaml | 7 +++++++ 9 files changed, 61 insertions(+) diff --git a/.github/workflows/apispec.yaml b/.github/workflows/apispec.yaml index 152f5b23c..b35e1d0c5 100644 --- a/.github/workflows/apispec.yaml +++ b/.github/workflows/apispec.yaml @@ -15,6 +15,13 @@ jobs: with: fetch-depth: 2 + - name: Cache vendor + uses: actions/cache@v5 + with: + path: vendor + key: vendor-php8.4-${{ hashFiles('composer.lock') }} + restore-keys: vendor-php8.4- + - name: Setup network run: docker network create frontend diff --git a/.github/workflows/code-analysis.yaml b/.github/workflows/code-analysis.yaml index 0f723816b..910a3162a 100644 --- a/.github/workflows/code-analysis.yaml +++ b/.github/workflows/code-analysis.yaml @@ -13,6 +13,13 @@ jobs: - name: Checkout uses: actions/checkout@v6 + - name: Cache vendor + uses: actions/cache@v5 + with: + path: vendor + key: vendor-php8.4-${{ hashFiles('composer.lock') }} + restore-keys: vendor-php8.4- + - name: Setup network run: docker network create frontend diff --git a/.github/workflows/composer.yaml b/.github/workflows/composer.yaml index 8787034df..5a63ec3ad 100644 --- a/.github/workflows/composer.yaml +++ b/.github/workflows/composer.yaml @@ -52,6 +52,12 @@ jobs: fail-fast: false steps: - uses: actions/checkout@v6 + - name: Cache vendor + uses: actions/cache@v5 + with: + path: vendor + key: vendor-php8.4-${{ hashFiles('composer.lock') }} + restore-keys: vendor-php8.4- - run: | docker network create frontend docker compose run --rm phpfpm composer install diff --git a/.github/workflows/doctrine.yaml b/.github/workflows/doctrine.yaml index dccba03b8..768dc1410 100644 --- a/.github/workflows/doctrine.yaml +++ b/.github/workflows/doctrine.yaml @@ -13,6 +13,13 @@ jobs: - name: Checkout uses: actions/checkout@v6 + - name: Cache vendor (prod) + uses: actions/cache@v5 + with: + path: vendor + key: vendor-prod-php8.4-${{ hashFiles('composer.lock') }} + restore-keys: vendor-prod-php8.4- + - name: Setup network run: docker network create frontend diff --git a/.github/workflows/php.yaml b/.github/workflows/php.yaml index 02dc8c532..fd24c09c4 100644 --- a/.github/workflows/php.yaml +++ b/.github/workflows/php.yaml @@ -49,6 +49,12 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 + - name: Cache vendor + uses: actions/cache@v5 + with: + path: vendor + key: vendor-php8.4-${{ hashFiles('composer.lock') }} + restore-keys: vendor-php8.4- - run: | docker network create frontend docker compose run --rm phpfpm composer install diff --git a/.github/workflows/phpunit.yaml b/.github/workflows/phpunit.yaml index 9daee407b..7c8d515f6 100644 --- a/.github/workflows/phpunit.yaml +++ b/.github/workflows/phpunit.yaml @@ -13,6 +13,13 @@ jobs: - name: Checkout uses: actions/checkout@v6 + - name: Cache vendor + uses: actions/cache@v5 + with: + path: vendor + key: vendor-php8.4-${{ hashFiles('composer.lock') }} + restore-keys: vendor-php8.4- + - name: Setup network run: docker network create frontend diff --git a/.github/workflows/playwright.yaml b/.github/workflows/playwright.yaml index 93ae20714..4aa91aba2 100644 --- a/.github/workflows/playwright.yaml +++ b/.github/workflows/playwright.yaml @@ -15,6 +15,13 @@ jobs: - name: Checkout uses: actions/checkout@v6 + - name: Cache vendor + uses: actions/cache@v5 + with: + path: vendor + key: vendor-php8.4-${{ hashFiles('composer.lock') }} + restore-keys: vendor-php8.4- + - name: Setup network run: docker network create frontend diff --git a/.github/workflows/rector.yaml b/.github/workflows/rector.yaml index 47bacbc71..1f6b4f3c0 100644 --- a/.github/workflows/rector.yaml +++ b/.github/workflows/rector.yaml @@ -13,6 +13,13 @@ jobs: - name: Checkout uses: actions/checkout@v6 + - name: Cache vendor + uses: actions/cache@v5 + with: + path: vendor + key: vendor-php8.4-${{ hashFiles('composer.lock') }} + restore-keys: vendor-php8.4- + - name: Setup network run: docker network create frontend diff --git a/.github/workflows/twig.yaml b/.github/workflows/twig.yaml index 76e2a57d6..c5cf21f3b 100644 --- a/.github/workflows/twig.yaml +++ b/.github/workflows/twig.yaml @@ -42,6 +42,13 @@ jobs: - name: Checkout uses: actions/checkout@v6 + - name: Cache vendor + uses: actions/cache@v5 + with: + path: vendor + key: vendor-php8.4-${{ hashFiles('composer.lock') }} + restore-keys: vendor-php8.4- + - run: | docker network create frontend docker compose run --rm phpfpm composer install From 012a980aa8fabed5e279ebc7adc9ad67679889bf Mon Sep 17 00:00:00 2001 From: turegjorup Date: Fri, 1 May 2026 08:13:40 +0200 Subject: [PATCH 42/59] Skip mariadb startup for database-less PHP workflows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit phpfpm declares depends_on: mariadb (service_healthy), so every docker compose run on it pulls and boots mariadb plus waits for the healthcheck — wasted work for code-analysis, rector, twig-cs, php-cs-fixer, composer normalize/validate, and the prod-mode composer-install validator. --no-deps starts only the requested service. The compose file is unchanged; doctrine, phpunit, and playwright still pull mariadb because they actually need it. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/code-analysis.yaml | 4 ++-- .github/workflows/composer.yaml | 10 +++++----- .github/workflows/composer_install.yaml | 2 +- .github/workflows/php.yaml | 10 +++++----- .github/workflows/rector.yaml | 4 ++-- .github/workflows/twig.yaml | 6 +++--- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/code-analysis.yaml b/.github/workflows/code-analysis.yaml index 910a3162a..f3112087d 100644 --- a/.github/workflows/code-analysis.yaml +++ b/.github/workflows/code-analysis.yaml @@ -24,7 +24,7 @@ jobs: run: docker network create frontend - name: Install Dependencies - run: docker compose run --rm phpfpm composer install + run: docker compose run --rm --no-deps phpfpm composer install - name: PHPStan - run: docker compose run --rm phpfpm vendor/bin/phpstan analyse --no-progress + run: docker compose run --rm --no-deps phpfpm vendor/bin/phpstan analyse --no-progress diff --git a/.github/workflows/composer.yaml b/.github/workflows/composer.yaml index 5a63ec3ad..3a929951c 100644 --- a/.github/workflows/composer.yaml +++ b/.github/workflows/composer.yaml @@ -14,13 +14,13 @@ ### is a dev requirement in `composer.json`: ### ### ``` shell -### docker compose run --rm phpfpm composer require --dev ergebnis/composer-normalize +### docker compose run --rm --no-deps phpfpm composer require --dev ergebnis/composer-normalize ### ``` ### ### Normalize `composer.json` by running ### ### ``` shell -### docker compose run --rm phpfpm composer normalize +### docker compose run --rm --no-deps phpfpm composer normalize ### ``` name: Composer @@ -44,7 +44,7 @@ jobs: - uses: actions/checkout@v6 - run: | docker network create frontend - docker compose run --rm phpfpm composer validate --strict + docker compose run --rm --no-deps phpfpm composer validate --strict composer-normalized: runs-on: ubuntu-latest @@ -60,5 +60,5 @@ jobs: restore-keys: vendor-php8.4- - run: | docker network create frontend - docker compose run --rm phpfpm composer install - docker compose run --rm phpfpm composer normalize --dry-run + docker compose run --rm --no-deps phpfpm composer install + docker compose run --rm --no-deps phpfpm composer normalize --dry-run diff --git a/.github/workflows/composer_install.yaml b/.github/workflows/composer_install.yaml index bdce83d6f..4b51a4998 100644 --- a/.github/workflows/composer_install.yaml +++ b/.github/workflows/composer_install.yaml @@ -17,4 +17,4 @@ jobs: run: docker network create frontend - name: "[prod] Composer install" - run: docker compose run --rm -e APP_ENV=prod phpfpm composer install --no-dev -o + run: docker compose run --rm --no-deps -e APP_ENV=prod phpfpm composer install --no-dev -o diff --git a/.github/workflows/php.yaml b/.github/workflows/php.yaml index fd24c09c4..90e4b15fd 100644 --- a/.github/workflows/php.yaml +++ b/.github/workflows/php.yaml @@ -15,14 +15,14 @@ ### is a dev requirement in `composer.json`: ### ### ``` shell -### docker compose run --rm phpfpm composer require --dev friendsofphp/php-cs-fixer +### docker compose run --rm --no-deps phpfpm composer require --dev friendsofphp/php-cs-fixer ### ``` ### ### Clean up and check code by running ### ### ``` shell -### docker compose run --rm phpfpm vendor/bin/php-cs-fixer fix -### docker compose run --rm phpfpm vendor/bin/php-cs-fixer fix --dry-run --diff +### docker compose run --rm --no-deps phpfpm vendor/bin/php-cs-fixer fix +### docker compose run --rm --no-deps phpfpm vendor/bin/php-cs-fixer fix --dry-run --diff ### ``` ### ### > [!NOTE] The template adds `.php-cs-fixer.dist.php` as [a configuration @@ -57,6 +57,6 @@ jobs: restore-keys: vendor-php8.4- - run: | docker network create frontend - docker compose run --rm phpfpm composer install + docker compose run --rm --no-deps phpfpm composer install # https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/blob/master/doc/usage.rst#the-check-command - docker compose run --rm phpfpm vendor/bin/php-cs-fixer fix --dry-run --diff + docker compose run --rm --no-deps phpfpm vendor/bin/php-cs-fixer fix --dry-run --diff diff --git a/.github/workflows/rector.yaml b/.github/workflows/rector.yaml index 1f6b4f3c0..821e85f9b 100644 --- a/.github/workflows/rector.yaml +++ b/.github/workflows/rector.yaml @@ -24,7 +24,7 @@ jobs: run: docker network create frontend - name: Install Dependencies - run: docker compose run --rm phpfpm composer install + run: docker compose run --rm --no-deps phpfpm composer install - name: Rector - run: docker compose run --rm phpfpm vendor/bin/rector --dry-run + run: docker compose run --rm --no-deps phpfpm vendor/bin/rector --dry-run diff --git a/.github/workflows/twig.yaml b/.github/workflows/twig.yaml index c5cf21f3b..b2b1e35f0 100644 --- a/.github/workflows/twig.yaml +++ b/.github/workflows/twig.yaml @@ -14,7 +14,7 @@ ### is a dev requirement in `composer.json`: ### ### ``` shell -### docker compose run --rm phpfpm composer require --dev vincentlanglet/twig-cs-fixer +### docker compose run --rm --no-deps phpfpm composer require --dev vincentlanglet/twig-cs-fixer ### ``` ### ### 3. A [Configuration @@ -51,5 +51,5 @@ jobs: - run: | docker network create frontend - docker compose run --rm phpfpm composer install - docker compose run --rm phpfpm vendor/bin/twig-cs-fixer lint + docker compose run --rm --no-deps phpfpm composer install + docker compose run --rm --no-deps phpfpm vendor/bin/twig-cs-fixer lint From ef586bf5423700ec9e31a1c7fd90381e6d893bfe Mon Sep 17 00:00:00 2001 From: turegjorup Date: Fri, 1 May 2026 08:14:09 +0200 Subject: [PATCH 43/59] Cache node_modules in JS CI workflows Vitest spends ~31s of its ~34s wall time on a cold npm install; Playwright pays the same toll. actions/cache keyed on package-lock.json turns the install into a near no-op when the lockfile is unchanged. node_modules lives at the repo root and is bind-mounted into the node service via .:/app, so the docker compose setup is unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/playwright.yaml | 7 +++++++ .github/workflows/vitest.yaml | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/.github/workflows/playwright.yaml b/.github/workflows/playwright.yaml index 4aa91aba2..031826e42 100644 --- a/.github/workflows/playwright.yaml +++ b/.github/workflows/playwright.yaml @@ -22,6 +22,13 @@ jobs: key: vendor-php8.4-${{ hashFiles('composer.lock') }} restore-keys: vendor-php8.4- + - name: Cache node_modules + uses: actions/cache@v5 + with: + path: node_modules + key: node-modules-${{ hashFiles('package-lock.json') }} + restore-keys: node-modules- + - name: Setup network run: docker network create frontend diff --git a/.github/workflows/vitest.yaml b/.github/workflows/vitest.yaml index bda6ac97f..f20a92769 100644 --- a/.github/workflows/vitest.yaml +++ b/.github/workflows/vitest.yaml @@ -13,6 +13,13 @@ jobs: - name: Checkout uses: actions/checkout@v6 + - name: Cache node_modules + uses: actions/cache@v5 + with: + path: node_modules + key: node-modules-${{ hashFiles('package-lock.json') }} + restore-keys: node-modules- + - name: Setup network run: docker network create frontend From 016209fce99338499d9b038963ff34ff3ac40f2b Mon Sep 17 00:00:00 2001 From: turegjorup Date: Fri, 1 May 2026 08:16:37 +0200 Subject: [PATCH 44/59] Drop no-op strategy/fail-fast blocks from non-matrix jobs fail-fast only applies to matrix-strategy jobs; declaring it on a single-job workflow is dead config that adds reading overhead and implies a matrix where there is none. Stripping it from the seven workflows that carry it without any matrix:. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/changelog.yaml | 2 -- .github/workflows/composer.yaml | 4 ---- .github/workflows/javascript.yaml | 2 -- .github/workflows/markdown.yaml | 2 -- .github/workflows/playwright.yaml | 2 -- .github/workflows/styles.yaml | 2 -- .github/workflows/twig.yaml | 2 -- 7 files changed, 16 deletions(-) diff --git a/.github/workflows/changelog.yaml b/.github/workflows/changelog.yaml index 63638c2d1..69086676f 100644 --- a/.github/workflows/changelog.yaml +++ b/.github/workflows/changelog.yaml @@ -14,8 +14,6 @@ on: jobs: changelog: runs-on: ubuntu-latest - strategy: - fail-fast: false steps: - name: Checkout uses: actions/checkout@v6 diff --git a/.github/workflows/composer.yaml b/.github/workflows/composer.yaml index 3a929951c..8ff4e7ba0 100644 --- a/.github/workflows/composer.yaml +++ b/.github/workflows/composer.yaml @@ -38,8 +38,6 @@ on: jobs: composer-validate: runs-on: ubuntu-latest - strategy: - fail-fast: false steps: - uses: actions/checkout@v6 - run: | @@ -48,8 +46,6 @@ jobs: composer-normalized: runs-on: ubuntu-latest - strategy: - fail-fast: false steps: - uses: actions/checkout@v6 - name: Cache vendor diff --git a/.github/workflows/javascript.yaml b/.github/workflows/javascript.yaml index 4dacffe6e..125f948cd 100644 --- a/.github/workflows/javascript.yaml +++ b/.github/workflows/javascript.yaml @@ -23,8 +23,6 @@ on: jobs: javascript-lint: runs-on: ubuntu-latest - strategy: - fail-fast: false steps: - name: Checkout uses: actions/checkout@v6 diff --git a/.github/workflows/markdown.yaml b/.github/workflows/markdown.yaml index 066063a5c..5ac18ca10 100644 --- a/.github/workflows/markdown.yaml +++ b/.github/workflows/markdown.yaml @@ -30,8 +30,6 @@ on: jobs: markdown-lint: runs-on: ubuntu-latest - strategy: - fail-fast: false steps: - name: Checkout uses: actions/checkout@v6 diff --git a/.github/workflows/playwright.yaml b/.github/workflows/playwright.yaml index 031826e42..82577187a 100644 --- a/.github/workflows/playwright.yaml +++ b/.github/workflows/playwright.yaml @@ -9,8 +9,6 @@ jobs: frontend-build-and-test: name: Playwright runs-on: ubuntu-latest - strategy: - fail-fast: true steps: - name: Checkout uses: actions/checkout@v6 diff --git a/.github/workflows/styles.yaml b/.github/workflows/styles.yaml index 02f9d6bcd..086766b33 100644 --- a/.github/workflows/styles.yaml +++ b/.github/workflows/styles.yaml @@ -23,8 +23,6 @@ on: jobs: styles-lint: runs-on: ubuntu-latest - strategy: - fail-fast: false steps: - name: Checkout uses: actions/checkout@v6 diff --git a/.github/workflows/twig.yaml b/.github/workflows/twig.yaml index b2b1e35f0..da800b82b 100644 --- a/.github/workflows/twig.yaml +++ b/.github/workflows/twig.yaml @@ -36,8 +36,6 @@ on: jobs: twig-lint: runs-on: ubuntu-latest - strategy: - fail-fast: false steps: - name: Checkout uses: actions/checkout@v6 From 200654b8e0e9cc137a69405fe68c8873b2bb2888 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Fri, 1 May 2026 08:31:34 +0200 Subject: [PATCH 45/59] Drop itk-dev tooling header, document every workflow uniformly Eight workflows carried an itk-dev/devops_itkdev-docker "do not edit" header that no longer applies in this repo. Stripping the three-line header preserves the existing ### ### Title / Description / Assumptions docs that follow it. The remaining eleven workflows had no header documentation; they get the same shape (title, one-paragraph purpose, Assumptions list) so a reader can land on any workflow file and learn what it does and what it relies on without leaving the file. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/apispec.yaml | 13 +++++++++++++ .github/workflows/build-images.yml | 13 +++++++++++++ .github/workflows/changelog.yaml | 4 ---- .github/workflows/code-analysis.yaml | 12 ++++++++++++ .github/workflows/composer.yaml | 4 ---- .github/workflows/composer_install.yaml | 10 ++++++++++ .github/workflows/doctrine.yaml | 11 +++++++++++ .github/workflows/env_coverage.yaml | 10 ++++++++++ .github/workflows/github_build_release.yml | 11 +++++++++++ .github/workflows/javascript.yaml | 4 ---- .github/workflows/markdown.yaml | 4 ---- .github/workflows/php.yaml | 4 ---- .github/workflows/phpunit.yaml | 11 +++++++++++ .github/workflows/playwright.yaml | 12 ++++++++++++ .github/workflows/rector.yaml | 12 ++++++++++++ .github/workflows/styles.yaml | 4 ---- .github/workflows/twig.yaml | 4 ---- .github/workflows/vitest.yaml | 10 ++++++++++ .github/workflows/yaml.yaml | 4 ---- 19 files changed, 125 insertions(+), 32 deletions(-) diff --git a/.github/workflows/apispec.yaml b/.github/workflows/apispec.yaml index b35e1d0c5..fd86467ef 100644 --- a/.github/workflows/apispec.yaml +++ b/.github/workflows/apispec.yaml @@ -1,3 +1,16 @@ +### ### Api Spec +### +### Re-exports the OpenAPI spec (`public/api-spec-v2.{yaml,json}`) from the +### API Platform configuration and fails if the result differs from the +### committed copy. +### +### #### Assumptions +### +### 1. A docker compose service named `phpfpm` can be run and `bin/console` +### can be run inside the `phpfpm` service. +### 2. The committed `public/api-spec-v2.{yaml,json}` are kept in sync with +### the API Platform config. + on: pull_request name: Api Spec diff --git a/.github/workflows/build-images.yml b/.github/workflows/build-images.yml index cd45bc912..2560d08d9 100644 --- a/.github/workflows/build-images.yml +++ b/.github/workflows/build-images.yml @@ -1,3 +1,16 @@ +### ### Build docker images +### +### Builds and pushes the API and Nginx images to GHCR. Triggers on push to +### `develop` (image tag `develop`) and on git tag pushes (image tag = git +### tag). The Nginx image layers on the API image, so the API build runs +### first in the same job. +### +### #### Assumptions +### +### 1. The `os2display` GHCR namespace exists and `GITHUB_TOKEN` carries +### `packages: write`. +### 2. `infrastructure/build-n-push.sh` mirrors the build for local use. + --- name: Build docker images diff --git a/.github/workflows/changelog.yaml b/.github/workflows/changelog.yaml index 69086676f..327d42953 100644 --- a/.github/workflows/changelog.yaml +++ b/.github/workflows/changelog.yaml @@ -1,7 +1,3 @@ -# Do not edit this file! Make a pull request on changing -# github/workflows/changelog.yaml in -# https://github.com/itk-dev/devops_itkdev-docker if need be. - ### ### Changelog ### ### Checks that changelog has been updated diff --git a/.github/workflows/code-analysis.yaml b/.github/workflows/code-analysis.yaml index f3112087d..c8a276435 100644 --- a/.github/workflows/code-analysis.yaml +++ b/.github/workflows/code-analysis.yaml @@ -1,3 +1,15 @@ +### ### Code Analysis +### +### Runs PHPStan static analysis against the project. +### +### #### Assumptions +### +### 1. A docker compose service named `phpfpm` can be run and `composer` can +### be run inside the `phpfpm` service. +### 2. [phpstan/phpstan](https://phpstan.org/) is a dev requirement in +### `composer.json`. +### 3. `phpstan.dist.neon` configures the analyser. + on: pull_request name: Code Analysis diff --git a/.github/workflows/composer.yaml b/.github/workflows/composer.yaml index 8ff4e7ba0..c4818f9b5 100644 --- a/.github/workflows/composer.yaml +++ b/.github/workflows/composer.yaml @@ -1,7 +1,3 @@ -# Do not edit this file! Make a pull request on changing -# github/workflows/composer.yaml in -# https://github.com/itk-dev/devops_itkdev-docker if need be. - ### ### Composer ### ### Validates composer.json and checks that it's normalized. diff --git a/.github/workflows/composer_install.yaml b/.github/workflows/composer_install.yaml index 4b51a4998..157e4dd0d 100644 --- a/.github/workflows/composer_install.yaml +++ b/.github/workflows/composer_install.yaml @@ -1,3 +1,13 @@ +### ### Composer install +### +### Verifies that `composer install --no-dev -o` succeeds in prod mode against +### the committed lockfile. +### +### #### Assumptions +### +### 1. A docker compose service named `phpfpm` can be run and `composer` can +### be run inside the `phpfpm` service. + on: pull_request name: Composer install diff --git a/.github/workflows/doctrine.yaml b/.github/workflows/doctrine.yaml index 768dc1410..4a2877110 100644 --- a/.github/workflows/doctrine.yaml +++ b/.github/workflows/doctrine.yaml @@ -1,3 +1,14 @@ +### ### Doctrine +### +### Runs pending Doctrine migrations and validates the resulting schema +### against the entity metadata. +### +### #### Assumptions +### +### 1. A docker compose service named `phpfpm` can be run and `bin/console` +### can be run inside the `phpfpm` service. +### 2. A `mariadb` service is reachable from `phpfpm`. + on: pull_request name: Doctrine diff --git a/.github/workflows/env_coverage.yaml b/.github/workflows/env_coverage.yaml index 5a6dc18f8..8c08be02d 100644 --- a/.github/workflows/env_coverage.yaml +++ b/.github/workflows/env_coverage.yaml @@ -1,3 +1,13 @@ +### ### Env coverage +### +### Validates that every Symfony env variable read via `%env(...)%` in +### `config/` is documented in `.env`, and that every variable in `.env` +### carries a preceding description comment. +### +### #### Assumptions +### +### 1. `scripts/check-env-coverage.sh` is executable. +### 2. The Taskfile target `coding-standards:env:check` invokes it. --- on: push: diff --git a/.github/workflows/github_build_release.yml b/.github/workflows/github_build_release.yml index fc724d36b..19aecc2fd 100644 --- a/.github/workflows/github_build_release.yml +++ b/.github/workflows/github_build_release.yml @@ -1,3 +1,14 @@ +### ### Create Github Release +### +### On a semver tag push (`*.*.*`): builds the production tarball, writes +### `release.json`, and creates a GitHub release. Tags containing a `-` +### (e.g. `3.0.0-RC1`) are flagged as pre-releases. +### +### #### Assumptions +### +### 1. The `phpfpm` and `node` compose services can run on the runner. +### 2. The tagged commit passes the regular CI suite. + on: push: tags: diff --git a/.github/workflows/javascript.yaml b/.github/workflows/javascript.yaml index 125f948cd..8b7ed0a97 100644 --- a/.github/workflows/javascript.yaml +++ b/.github/workflows/javascript.yaml @@ -1,7 +1,3 @@ -# Do not edit this file! Make a pull request on changing -# github/workflows/symfony/javascript.yaml in -# https://github.com/itk-dev/devops_itkdev-docker if need be. - ### ### Symfony JavaScript (and TypeScript) ### ### Validates JavaScript files. diff --git a/.github/workflows/markdown.yaml b/.github/workflows/markdown.yaml index 5ac18ca10..6e4f63e99 100644 --- a/.github/workflows/markdown.yaml +++ b/.github/workflows/markdown.yaml @@ -1,7 +1,3 @@ -# Do not edit this file! Make a pull request on changing -# github/workflows/markdown.yaml in -# https://github.com/itk-dev/devops_itkdev-docker if need be. - ### ### Markdown ### ### Lints Markdown files (`**/*.md`) in the project. diff --git a/.github/workflows/php.yaml b/.github/workflows/php.yaml index 90e4b15fd..bbbfc5c3b 100644 --- a/.github/workflows/php.yaml +++ b/.github/workflows/php.yaml @@ -1,7 +1,3 @@ -# Do not edit this file! Make a pull request on changing -# github/workflows/symfony/php.yaml in -# https://github.com/itk-dev/devops_itkdev-docker if need be. - ### ### Symfony PHP ### ### Checks that PHP code adheres to the [Symfony coding diff --git a/.github/workflows/phpunit.yaml b/.github/workflows/phpunit.yaml index 7c8d515f6..87f259d8d 100644 --- a/.github/workflows/phpunit.yaml +++ b/.github/workflows/phpunit.yaml @@ -1,3 +1,14 @@ +### ### Test (PHPUnit) +### +### Runs the API's PHPUnit test suite against a real database. +### +### #### Assumptions +### +### 1. A docker compose service named `phpfpm` can be run and `composer` can +### be run inside the `phpfpm` service. +### 2. A `mariadb` service is reachable from `phpfpm`. +### 3. `composer run test-setup` and `composer run test` are defined. + on: pull_request name: Test diff --git a/.github/workflows/playwright.yaml b/.github/workflows/playwright.yaml index 82577187a..6cc614d80 100644 --- a/.github/workflows/playwright.yaml +++ b/.github/workflows/playwright.yaml @@ -1,3 +1,15 @@ +### ### Playwright +### +### Builds the frontend (`composer install`, `npm install`, `npm run build`) +### and runs the Playwright end-to-end test suite against the local stack. +### +### #### Assumptions +### +### 1. Docker compose services `phpfpm`, `node`, `nginx`, and `playwright` +### are defined. +### 2. The `nginx` service has a healthcheck Playwright can wait on. +### 3. `playwright.config.ts` lives in the repo root. + on: pull_request name: Test diff --git a/.github/workflows/rector.yaml b/.github/workflows/rector.yaml index 821e85f9b..0e28e9052 100644 --- a/.github/workflows/rector.yaml +++ b/.github/workflows/rector.yaml @@ -1,3 +1,15 @@ +### ### Rector +### +### Runs Rector in `--dry-run` mode to detect missing modernisations. +### +### #### Assumptions +### +### 1. A docker compose service named `phpfpm` can be run and `composer` can +### be run inside the `phpfpm` service. +### 2. [rector/rector](https://github.com/rectorphp/rector) is a dev +### requirement in `composer.json`. +### 3. `rector.php` configures the rule set. + on: pull_request name: Rector diff --git a/.github/workflows/styles.yaml b/.github/workflows/styles.yaml index 086766b33..3b4bf27bb 100644 --- a/.github/workflows/styles.yaml +++ b/.github/workflows/styles.yaml @@ -1,7 +1,3 @@ -# Do not edit this file! Make a pull request on changing -# github/workflows/symfony/styles.yaml in -# https://github.com/itk-dev/devops_itkdev-docker if need be. - ### ### Symfony Styles (CSS and SCSS) ### ### Validates styles files. diff --git a/.github/workflows/twig.yaml b/.github/workflows/twig.yaml index da800b82b..fbf799c89 100644 --- a/.github/workflows/twig.yaml +++ b/.github/workflows/twig.yaml @@ -1,7 +1,3 @@ -# Do not edit this file! Make a pull request on changing -# github/workflows/twig.yaml in -# https://github.com/itk-dev/devops_itkdev-docker if need be. - ### ### Twig ### ### Validates Twig files diff --git a/.github/workflows/vitest.yaml b/.github/workflows/vitest.yaml index f20a92769..ddb3e1a61 100644 --- a/.github/workflows/vitest.yaml +++ b/.github/workflows/vitest.yaml @@ -1,3 +1,13 @@ +### ### Unit tests (Vitest) +### +### Runs the client-side Vitest unit tests. +### +### #### Assumptions +### +### 1. A docker compose service named `node` can be run and `npm` can be run +### inside the `node` service. +### 2. `npm run test:unit` is defined in `package.json`. + on: pull_request name: Unit tests diff --git a/.github/workflows/yaml.yaml b/.github/workflows/yaml.yaml index 16c44b9ff..349b55752 100644 --- a/.github/workflows/yaml.yaml +++ b/.github/workflows/yaml.yaml @@ -1,7 +1,3 @@ -# Do not edit this file! Make a pull request on changing -# github/workflows/yaml.yaml in -# https://github.com/itk-dev/devops_itkdev-docker if need be. - ### ### YAML ### ### Validates YAML files. From b0be84c95b25e3b36c5ec8b58d30580cde5e485e Mon Sep 17 00:00:00 2001 From: turegjorup Date: Fri, 1 May 2026 08:34:56 +0200 Subject: [PATCH 46/59] Comment on PR only when api-spec drifts or changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adapts itk-dev/devops_itksites's two-job api-spec workflow to this repo's v2 spec layout, with one behaviour change: stay quiet when the spec is unchanged. itksites' "no changes detected" comment lands on every PR that touches src/, which is most of them; suppressing that branch removes the noise while keeping the signal — the workflow now only posts on (a) committed spec out of date with the source, (b) non-breaking spec changes (changelog), or (c) breaking spec changes (changelog + failure). --edit-last keeps the PR to one in-place comment across pushes. Pinned oasdiff-action to v0.0.44 instead of @main; path filters restrict the run to PRs that could plausibly affect the spec; the breaking-changes job depends on the export job so a drifting spec isn't compared against the base. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/apispec.yaml | 138 +++++++++++++++++++++++++++++---- 1 file changed, 122 insertions(+), 16 deletions(-) diff --git a/.github/workflows/apispec.yaml b/.github/workflows/apispec.yaml index fd86467ef..abfd6c496 100644 --- a/.github/workflows/apispec.yaml +++ b/.github/workflows/apispec.yaml @@ -1,17 +1,28 @@ ### ### Api Spec ### ### Re-exports the OpenAPI spec (`public/api-spec-v2.{yaml,json}`) from the -### API Platform configuration and fails if the result differs from the -### committed copy. +### API Platform configuration. Posts a PR comment **only** when something +### needs attention — spec drift, non-breaking changes, or breaking changes — +### and stays quiet otherwise. The comment is edited in place across runs +### so a PR carries at most one comment from this workflow. ### ### #### Assumptions ### ### 1. A docker compose service named `phpfpm` can be run and `bin/console` ### can be run inside the `phpfpm` service. ### 2. The committed `public/api-spec-v2.{yaml,json}` are kept in sync with -### the API Platform config. +### the API Platform configuration via `composer update-api-spec`. -on: pull_request +on: + pull_request: + paths: + - "src/**/*.php" + - "config/**" + - "composer.json" + - "composer.lock" + - "public/api-spec-v2.yaml" + - "public/api-spec-v2.json" + - "docker-compose.yml" name: Api Spec @@ -19,14 +30,15 @@ env: COMPOSE_USER: runner jobs: - apispec: + api-spec-export: + name: Ensure API specification is up to date runs-on: ubuntu-latest - name: API Specification validation + permissions: + contents: read + pull-requests: write steps: - name: Checkout uses: actions/checkout@v6 - with: - fetch-depth: 2 - name: Cache vendor uses: actions/cache@v5 @@ -41,14 +53,108 @@ jobs: - name: Install Dependencies run: docker compose run --rm phpfpm composer install - - name: Export specifications (yaml) - run: docker compose run --rm phpfpm bin/console api:openapi:export --yaml --output=public/api-spec-v2.yaml --no-interaction + - name: Export specifications + run: | + docker compose run --rm phpfpm bin/console api:openapi:export --yaml --output=public/api-spec-v2.yaml --no-interaction + docker compose run --rm phpfpm bin/console api:openapi:export --output=public/api-spec-v2.json --no-interaction + + - name: Check for uncommitted changes + id: git-diff-spec + continue-on-error: true + run: git diff --diff-filter=ACMRT --exit-code public/api-spec-v2.yaml public/api-spec-v2.json + + - name: Comment PR if spec is outdated + if: steps.git-diff-spec.outcome == 'failure' + env: + GH_TOKEN: ${{ github.token }} + run: | + gh pr comment ${{ github.event.pull_request.number }} \ + --body "$(cat <<'EOF' + ## API specification not up to date + + The committed API specification files do not match the exported output. + + Please run the following command, then commit and push the changes: + + ```shell + docker compose exec phpfpm composer update-api-spec + ``` + EOF + )" \ + --create-if-none --edit-last + + - name: Fail if spec is outdated + if: steps.git-diff-spec.outcome == 'failure' + run: exit 1 + + api-spec-breaking-changes: + name: Detect breaking changes in API specification + runs-on: ubuntu-latest + needs: [api-spec-export] + permissions: + contents: read + pull-requests: write + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Fetch base branch for comparison + run: git fetch --depth=1 origin ${{ github.base_ref }} + + - name: Detect breaking changes + id: breaking + continue-on-error: true + uses: oasdiff/oasdiff-action/breaking@v0.0.44 + with: + base: "origin/${{ github.base_ref }}:public/api-spec-v2.yaml" + revision: "public/api-spec-v2.yaml" + fail-on: ERR + + - name: Generate changelog + id: changelog + continue-on-error: true + uses: oasdiff/oasdiff-action/changelog@v0.0.44 + with: + base: "origin/${{ github.base_ref }}:public/api-spec-v2.yaml" + revision: "public/api-spec-v2.yaml" + format: markdown + output-to-file: changelog.md + + # Stay quiet when nothing changed — neither breaking nor non-breaking. + # Comments only fire on the two branches below. - - name: Check for changes in specifications (yaml) - run: git diff --diff-filter=ACMRT --exit-code public/api-spec-v2.yaml + - name: Comment PR - non-breaking changes + if: steps.breaking.outcome == 'success' && hashFiles('changelog.md') != '' + env: + GH_TOKEN: ${{ github.token }} + run: | + { + echo "## API Specification - Non-breaking changes" + echo "" + cat changelog.md + } > comment.md + gh pr comment ${{ github.event.pull_request.number }} \ + --body-file comment.md \ + --create-if-none --edit-last - - name: Export specifications (json) - run: docker compose run --rm phpfpm bin/console api:openapi:export --output=public/api-spec-v2.json --no-interaction + - name: Comment PR - breaking changes + if: steps.breaking.outcome == 'failure' + env: + GH_TOKEN: ${{ github.token }} + run: | + { + echo "## API Specification - Breaking changes detected" + echo "" + if [ -s changelog.md ]; then + cat changelog.md + else + echo "The breaking changes action detected incompatible changes. Review the action logs for details." + fi + } > comment.md + gh pr comment ${{ github.event.pull_request.number }} \ + --body-file comment.md \ + --create-if-none --edit-last - - name: Check for changes in specifications (json) - run: git diff --diff-filter=ACMRT --exit-code public/api-spec-v2.json + - name: Fail if breaking changes detected + if: steps.breaking.outcome == 'failure' + run: exit 1 From 1501b0a5d6765427abada6245fdd65539e4e6992 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Fri, 1 May 2026 09:00:26 +0200 Subject: [PATCH 47/59] Re-run CI to measure cache-hot timings From cb3d3df93217b4fb4c828a709ca2c973a4dac4ea Mon Sep 17 00:00:00 2001 From: turegjorup Date: Fri, 1 May 2026 09:21:09 +0200 Subject: [PATCH 48/59] Update changelog for CI cleanup Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab00d4529..52d4d1b4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +- Cleaned up and documented CI workflows; api-spec workflow now reports breaking changes. - Gathered all repositories in one Symfony application. - Changed to vite 7 and rolldown. - Added ADRs 008 and 009. From de90a116dac801ae68d630edecd671d7f73b997a Mon Sep 17 00:00:00 2001 From: turegjorup Date: Fri, 1 May 2026 10:02:25 +0200 Subject: [PATCH 49/59] Update ITK Dev contact email to ba.aarhus.dk The mkb.aarhus.dk address is no longer the right inbox; ba.aarhus.dk is the current ITK Dev team mailbox. Updates the four LABEL maintainer lines in the API + Nginx Dockerfiles and the API Platform OpenAPI contact configuration so all surfaces point at the same place. Co-Authored-By: Claude Opus 4.7 (1M context) --- config/packages/api_platform.yaml | 2 +- infrastructure/display-api-service/Dockerfile | 6 +++--- infrastructure/nginx/Dockerfile | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/config/packages/api_platform.yaml b/config/packages/api_platform.yaml index 179834b74..274fc66ba 100644 --- a/config/packages/api_platform.yaml +++ b/config/packages/api_platform.yaml @@ -48,7 +48,7 @@ api_platform: openapi: contact: name: ITKDev - email: itkdev@mkb.aarhus.dk + email: itkdev@ba.aarhus.dk license: name: MIT diff --git a/infrastructure/display-api-service/Dockerfile b/infrastructure/display-api-service/Dockerfile index 5491df3fa..127629db7 100644 --- a/infrastructure/display-api-service/Dockerfile +++ b/infrastructure/display-api-service/Dockerfile @@ -1,6 +1,6 @@ ######## Clients [Screen|Admin] build ######## FROM node:24-alpine AS client_app_builder -LABEL maintainer="ITK Dev " +LABEL maintainer="ITK Dev " ARG APP_VERSION="develop" ARG APP_RELEASE_TIMESTAMP=0 @@ -24,7 +24,7 @@ RUN npm run build ######### API backend build ######## FROM itkdev/php8.4-fpm:alpine AS api_app_builder -LABEL maintainer="ITK Dev " +LABEL maintainer="ITK Dev " ARG APP_VERSION="develop" ARG APP_RELEASE_TIMESTAMP=0 @@ -69,7 +69,7 @@ RUN printf '{"releaseTimestamp": %s, "releaseTime": "%s", "releaseVersion": "%s" ######## PHP-FPM (API) production image ######## FROM itkdev/php8.4-fpm:alpine -LABEL maintainer="ITK Dev " +LABEL maintainer="ITK Dev " ENV APP_CLIENT_PATH=/app \ APP_API_PATH=/var/www/html \ diff --git a/infrastructure/nginx/Dockerfile b/infrastructure/nginx/Dockerfile index ae2d2d309..6ca34543d 100644 --- a/infrastructure/nginx/Dockerfile +++ b/infrastructure/nginx/Dockerfile @@ -10,7 +10,7 @@ FROM ${APP_IMAGE}:${APP_VERSION} AS app ######## Nginx production image ######## FROM nginxinc/nginx-unprivileged:alpine -LABEL maintainer="ITK Dev " +LABEL maintainer="ITK Dev " ARG UID=101 From c12e1309618fe27093455fed3b4cbd8d369cea79 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Fri, 1 May 2026 10:44:15 +0200 Subject: [PATCH 50/59] Drop APP_API_PATH env from nginx image Used only at build time (WORKDIR + COPY source) and not consumed by the nginx runtime template. Inlining /var/www/html in both places removes the indirection. Co-Authored-By: Claude Opus 4.7 (1M context) --- infrastructure/nginx/Dockerfile | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/infrastructure/nginx/Dockerfile b/infrastructure/nginx/Dockerfile index 6ca34543d..06d99e278 100644 --- a/infrastructure/nginx/Dockerfile +++ b/infrastructure/nginx/Dockerfile @@ -14,19 +14,18 @@ LABEL maintainer="ITK Dev " ARG UID=101 -ENV APP_API_PATH=/var/www/html \ - NGINX_PORT=8080 \ +ENV NGINX_PORT=8080 \ NGINX_FPM_SERVICE=os2display \ NGINX_FPM_PORT=9000 \ NGINX_MAX_BODY_SIZE=140m \ NGINX_SET_REAL_IP_FROM=172.16.0.0/12 \ NGINX_WEB_ROOT=/var/www/html/public -WORKDIR ${APP_API_PATH} +WORKDIR /var/www/html # Single source of truth: public/ (incl. build/ and release.json) comes # from the just-published API image, not a duplicated builder stage. -COPY --from=app --chown=$UID:0 --chmod=775 ${APP_API_PATH}/public ./public +COPY --from=app --chown=$UID:0 --chmod=775 /var/www/html/public ./public # Copy nginx configuration COPY --chown=$UID:0 etc /etc/nginx From df89451becdb45e57d09168c9450b25853289a0c Mon Sep 17 00:00:00 2001 From: turegjorup Date: Fri, 1 May 2026 10:45:09 +0200 Subject: [PATCH 51/59] Document the nginx ENV block as envsubst defaults Every variable is referenced from default.conf.template; the comment makes that contract self-evident at the declaration site. Co-Authored-By: Claude Opus 4.7 (1M context) --- infrastructure/nginx/Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/infrastructure/nginx/Dockerfile b/infrastructure/nginx/Dockerfile index 06d99e278..e7ba89493 100644 --- a/infrastructure/nginx/Dockerfile +++ b/infrastructure/nginx/Dockerfile @@ -14,6 +14,8 @@ LABEL maintainer="ITK Dev " ARG UID=101 +# Defaults for the envsubst pass over /etc/nginx/templates/default.conf.template +# at container start; every value below is referenced from that template. ENV NGINX_PORT=8080 \ NGINX_FPM_SERVICE=os2display \ NGINX_FPM_PORT=9000 \ From 89f86cdd28d7349cc6e8e04af90aef502d18c37e Mon Sep 17 00:00:00 2001 From: turegjorup Date: Fri, 1 May 2026 10:55:35 +0200 Subject: [PATCH 52/59] Rename build-n-push.sh and require PUSH=1 to publish Default behaviour is now host-platform build + --load into the local daemon. Multi-arch + --push to the registry only fires when PUSH=1 is set explicitly, so a bare invocation can no longer publish to GHCR by accident. Co-Authored-By: Claude Opus 4.7 (1M context) --- infrastructure/Readme.md | 14 ++++++++++---- infrastructure/{build-n-push.sh => build.sh} | 16 ++++++++-------- 2 files changed, 18 insertions(+), 12 deletions(-) rename infrastructure/{build-n-push.sh => build.sh} (87%) diff --git a/infrastructure/Readme.md b/infrastructure/Readme.md index 4daf6b1ad..604f59ee6 100644 --- a/infrastructure/Readme.md +++ b/infrastructure/Readme.md @@ -18,7 +18,14 @@ Images are published to . ## Building images locally -A script is provided to build the images locally: `build-n-push.sh` +`infrastructure/build.sh` builds both images. By default it builds for the host platform and loads the result +into the local docker daemon. Set `PUSH=1` to build multi-arch and push to the registry — opting in keeps a bare +`sh infrastructure/build.sh` from accidentally publishing. + +```sh +sh infrastructure/build.sh # build + load locally +PUSH=1 sh infrastructure/build.sh # build multi-arch + push to GHCR +``` ## Build process @@ -28,6 +35,5 @@ and bakes the result into `/var/www/html`. The nginx image then layers on top of work runs once per CI run. Because the nginx build depends on the API image, the workflow builds and pushes the API image first; the nginx -step pulls the just-published manifest. For local builds, `build-n-push.sh` runs the same order, so when -`BUILD_LOAD=1` the API image is loaded into the local daemon under the same tag and BuildKit reuses it without a -network pull. +step pulls the just-published manifest. For local builds, `build.sh` runs the same order, so the API image is +loaded into the local daemon under the same tag and BuildKit reuses it without a network pull. diff --git a/infrastructure/build-n-push.sh b/infrastructure/build.sh similarity index 87% rename from infrastructure/build-n-push.sh rename to infrastructure/build.sh index 9f9a9da01..73c900b0a 100755 --- a/infrastructure/build-n-push.sh +++ b/infrastructure/build.sh @@ -6,20 +6,20 @@ REGISTRY="${REGISTRY:-ghcr.io/os2display}" RELEASE_TIMESTAMP="$(date +%s)" RELEASE_TIME="$(date -u)" -# Set BUILD_LOAD=1 to build for the host platform and load into the local -# docker daemon instead of pushing. Useful for local smoke tests; production -# builds (multi-arch + registry push) require BUILD_LOAD unset or 0. -if [ "${BUILD_LOAD:-0}" = "1" ]; then +# Default: build for the host platform and load into the local docker daemon. +# Set PUSH=1 to build multi-arch and push to the registry; opting in keeps a +# bare `sh build.sh` from accidentally publishing. +if [ "${PUSH:-0}" = "1" ]; then + PLATFORMS="--platform=linux/amd64,linux/arm64" + OUTPUT="--push" + NGINX_PULL="--pull" +else PLATFORMS="" OUTPUT="--load" # In load mode the nginx FROM points at the API image we just loaded into # the local daemon. --pull would force a registry lookup that fails (and # has no business pulling the just-built image anyway). NGINX_PULL="" -else - PLATFORMS="--platform=linux/amd64,linux/arm64" - OUTPUT="--push" - NGINX_PULL="--pull" fi # API (php-fpm) image. Context is the API infra dir so the Dockerfile picks From 934c8ab932d170ad8a7f183e0c9024001568d27c Mon Sep 17 00:00:00 2001 From: turegjorup Date: Fri, 1 May 2026 10:59:29 +0200 Subject: [PATCH 53/59] Add images:build and images:push tasks images:build wraps `sh infrastructure/build.sh` (load locally). images:push runs the same script with PUSH=1 and prompts for confirmation first, since publishing to GHCR is non-reversible. Co-Authored-By: Claude Opus 4.7 (1M context) --- Taskfile.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Taskfile.yml b/Taskfile.yml index ddd6a0e46..7097b346f 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -263,3 +263,14 @@ tasks: desc: "Build the assets." cmds: - task compose -- run --rm node npm run build + + images:build: + desc: "Build API and Nginx images locally (host platform, loaded into local docker daemon)." + cmds: + - sh infrastructure/build.sh + + images:push: + prompt: "This will publish multi-arch images to ghcr.io/os2display. Continue?" + desc: "Build multi-arch and push to GHCR. Requires `docker login ghcr.io` first." + cmds: + - PUSH=1 sh infrastructure/build.sh From 393cb3d0b21d30510a2dcd5dd975f1b23ca2fa64 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Fri, 1 May 2026 11:21:22 +0200 Subject: [PATCH 54/59] Simplify API Dockerfile, align stage names and section style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cleanup pass on the API image based on the canonical reference Dockerfile (itk-dev/os2display-api-service@0b2cc18) raised in PR review: - Renamed `client_app_builder` → `assets_builder` to match canonical. - Adopted canonical's section-divider comment block. - Dropped APP_CLIENT_PATH and APP_API_PATH env indirections; literal paths read better and match the FPM convention (/var/www/html). - Dropped the dead APP_RELEASE_TIMESTAMP arg in the assets stage. - Expanded inline comments to document motivation (two-pass composer install, vite manifest ordering, release.json purpose, runtime composer for entrypoint dump-env, Prometheus exporter, ENTRYPOINT vs CMD) — short lines, one fact each. Behaviour unchanged. Smoke build via `sh infrastructure/build.sh` loads working images; resulting API image still ships the PHP_OPCACHE_*/PHP_PM_* tuning and the public/build vite manifest + public/release.json that downstream consumers expect. Co-Authored-By: Claude Opus 4.7 (1M context) --- infrastructure/display-api-service/Dockerfile | 86 ++++++++++--------- 1 file changed, 47 insertions(+), 39 deletions(-) diff --git a/infrastructure/display-api-service/Dockerfile b/infrastructure/display-api-service/Dockerfile index 127629db7..148f0fa7c 100644 --- a/infrastructure/display-api-service/Dockerfile +++ b/infrastructure/display-api-service/Dockerfile @@ -1,65 +1,71 @@ -######## Clients [Screen|Admin] build ######## -FROM node:24-alpine AS client_app_builder +################################ +# Assets build +################################ +FROM node:24-alpine AS assets_builder LABEL maintainer="ITK Dev " ARG APP_VERSION="develop" -ARG APP_RELEASE_TIMESTAMP=0 -ENV APP_CLIENT_PATH=/app -WORKDIR ${APP_CLIENT_PATH} +WORKDIR /app -# Copy only necessary files for npm install +# Lockfile + vite config first so the npm install layer caches across +# unrelated source edits. COPY --from=repository-root package.json package-lock.json vite.config.js ./ - -# Install dependencies RUN npm ci --no-audit --no-fund -# Copy source files needed for build +# Source for `npm run build`. Vite emits to /app/public/build, which the +# api_app_builder stage copies out. COPY --from=repository-root assets/ ./assets/ COPY --from=repository-root public/client/ ./public/client/ - -# Build the application with version info RUN npm run build -######### API backend build ######## +################################ +# API backend build +################################ +# itkdev/php8.4-fpm publishes two parallel series: `2.x.y` (Ubuntu), +# tracked by `:latest`, and `alpine-1.4.x` (Alpine), tracked by +# `:alpine`. `:alpine` is therefore a moving alias to the newest +# `alpine-1.4.x` — convenient, but not reproducible across time. +# TODO: pin to a specific `alpine-1.4.x` tag (or a digest) once we +# settle on a baseline. Same caveat applies to the final stage below. FROM itkdev/php8.4-fpm:alpine AS api_app_builder LABEL maintainer="ITK Dev " ARG APP_VERSION="develop" ARG APP_RELEASE_TIMESTAMP=0 ARG APP_RELEASE_TIME="" -ENV APP_CLIENT_PATH=/app \ - APP_API_PATH=/var/www/html USER root -# Add composer in from the official composer image (also alpine). +# Pinned via the official composer image so the build doesn't depend on +# whatever composer happens to ship in the FPM base. COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer -WORKDIR ${APP_API_PATH} +WORKDIR /var/www/html USER deploy -# Copy only composer files first for better layer caching -COPY --chown=deploy:deploy --from=repository-root composer.json composer.lock symfony.lock ${APP_API_PATH}/ - -# Pre-install composer packages first (better image layer caching) - application code not present so have to be re-run +# Two-pass composer install: +# 1. Lockfile + composer.json only with --no-scripts. The vendor/ layer +# then survives unrelated source-code edits. +# 2. After the full source COPY, re-run with scripts to finalise the +# autoloader and trigger Symfony's cache:clear hook. +COPY --chown=deploy:deploy --from=repository-root composer.json composer.lock symfony.lock ./ RUN APP_ENV=prod composer install --no-dev -o --classmap-authoritative --no-scripts -# Copy application source (needed for build step) -COPY --chown=deploy:deploy --from=repository-root ./ ${APP_API_PATH}/ +COPY --chown=deploy:deploy --from=repository-root ./ ./ -# Copy javascript build files. This ensures that vite manifest files are availiable when "composer insatll" -# triggers a "cache:clear" enabling the vite bundle to generate cache configuration files +# Vite manifest must be in place before the second composer install +# triggers cache:clear, otherwise the vite bundle can't generate its +# cache config files. # @see https://symfony-vite.pentatrion.com/guide/performance.html#caching-configuration-files-%F0%9F%8F%83 -COPY --chown=deploy:deploy --from=client_app_builder ${APP_CLIENT_PATH}/public/build ./public/build +COPY --chown=deploy:deploy --from=assets_builder /app/public/build ./public/build -# Re-run composer install after application code copied to image to complete install RUN APP_ENV=prod composer install --no-dev --optimize-autoloader --classmap-authoritative -# Write release.json into the public root so the client (assets/shared/release-loader.js) -# can fetch it at /release.json. Shape matches docs/release-example.json. +# The client polls /release.json to detect when a screen should refresh; +# shape per docs/release-example.json. RUN printf '{"releaseTimestamp": %s, "releaseTime": "%s", "releaseVersion": "%s"}\n' \ "${APP_RELEASE_TIMESTAMP}" \ "${APP_RELEASE_TIME}" \ @@ -67,13 +73,13 @@ RUN printf '{"releaseTimestamp": %s, "releaseTime": "%s", "releaseVersion": "%s" > public/release.json -######## PHP-FPM (API) production image ######## +################################ +# PHP-FPM (API) production image +################################ FROM itkdev/php8.4-fpm:alpine LABEL maintainer="ITK Dev " -ENV APP_CLIENT_PATH=/app \ - APP_API_PATH=/var/www/html \ - APP_ENV=prod \ +ENV APP_ENV=prod \ PHP_OPCACHE_ENABLED=1 \ PHP_OPCACHE_VALIDATE_TIMESTAMPS=0 \ PHP_OPCACHE_MAX_ACCELERATED_FILES=20000 \ @@ -87,23 +93,25 @@ ENV APP_CLIENT_PATH=/app \ USER root -# Add composer needed to run optimizations after config is loaded. +# Needed at container start so the entrypoint can run +# `composer dump-env prod` against the operator's runtime env. COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer -# Download Prometheus php-fpm export. +# Prometheus exporter for php-fpm pool stats; scraped by ops monitoring. COPY --from=hipages/php-fpm_exporter:2.2.0 /php-fpm_exporter /usr/local/bin/php-fpm_exporter COPY --chmod=0755 docker-entrypoint.sh /usr/local/bin/ USER deploy -WORKDIR ${APP_API_PATH} +WORKDIR /var/www/html -# Install the api application. -COPY --chown=deploy:deploy --from=api_app_builder ${APP_API_PATH} . -RUN mkdir -p ./config/secrets +COPY --chown=deploy:deploy --from=api_app_builder /var/www/html . -WORKDIR ${APP_API_PATH} +# Mount point for the Symfony secrets vault. +RUN mkdir -p ./config/secrets +# Entrypoint compiles env vars to .env.local.php and warms the Symfony +# cache before exec'ing CMD; CMD stays overridable for ad-hoc commands. ENTRYPOINT ["docker-entrypoint.sh"] CMD ["php-fpm"] From ce479722526a2811a76f6a2f3bfc1b87ef43c2ca Mon Sep 17 00:00:00 2001 From: turegjorup Date: Fri, 1 May 2026 16:32:30 +0200 Subject: [PATCH 55/59] Document images in README, drop dead public/client COPY - Top-level README gains a "Container images" section that names the two published images, links to infrastructure/Readme.md for the build pipeline, and explains the env-var precedence operators should rely on (compose env_file/environment > .env.local.php). - infrastructure/Readme.md replaces the bare build script reference with the new task entries (images:build / images:push) and expands the "Build process" section into a numbered walk-through with the motivation for each step. - assets_builder no longer copies public/client/. Vite's entrypoints live under assets/; public/client/online-check/ is a static path the screen client polls at runtime, and reaches the final image via the api_app_builder's repo-root copy. The same is true for the public/media/thumbnail_*.png fallbacks that .dockerignore preserves. Comment moved to api_app_builder where the path actually matters. Smoke build with `sh infrastructure/build.sh` passes; final image still carries /var/www/html/public/client/online-check/{index.html, 1pixel.png} and the two thumbnail PNGs. Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 59 +++++++++++++------ infrastructure/Readme.md | 53 +++++++++++++---- infrastructure/display-api-service/Dockerfile | 11 +++- 3 files changed, 90 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index f3b2c9b8e..15f52997d 100644 --- a/README.md +++ b/README.md @@ -9,23 +9,24 @@ 5. [Taskfile](#taskfile) 6. [Development setup](#development-setup) 7. [Production setup](#production-setup) -8. [Coding standards](#coding-standards) -9. [Stateless](#stateless) -10. [OIDC providers](#oidc-providers) -11. [JWT Auth](#jwt-auth) -12. [Test](#test) -13. [API specification and generated code](#api-specification-and-generated-code) -14. [Configuration](#configuration) -15. [Rest API & Relationships](#rest-api--relationships) -16. [Error codes in the Client](#error-codes-in-the-client) -17. [Preview mode in the Client](#preview-mode-in-the-client) -18. [Feeds](#feeds) -19. [Custom Templates](#custom-templates) -20. [Static Analysis](#static-analysis) -21. [Upgrade Guide](#upgrade-guide) -22. [Tenants](#tenants) -23. [Screen layouts](#screen-layouts) -24. [Templates](#templates) +8. [Container images](#container-images) +9. [Coding standards](#coding-standards) +10. [Stateless](#stateless) +11. [OIDC providers](#oidc-providers) +12. [JWT Auth](#jwt-auth) +13. [Test](#test) +14. [API specification and generated code](#api-specification-and-generated-code) +15. [Configuration](#configuration) +16. [Rest API & Relationships](#rest-api--relationships) +17. [Error codes in the Client](#error-codes-in-the-client) +18. [Preview mode in the Client](#preview-mode-in-the-client) +19. [Feeds](#feeds) +20. [Custom Templates](#custom-templates) +21. [Static Analysis](#static-analysis) +22. [Upgrade Guide](#upgrade-guide) +23. [Tenants](#tenants) +24. [Screen layouts](#screen-layouts) +25. [Templates](#templates) ## Description @@ -155,14 +156,34 @@ APP_ENV=prod APP_SECRET= ``` -TODO: Add further production instructions: Build steps, release.json, etc. - Use the `app:update` command to migrate and update templates to latest version: ```shell docker compose exec phpfpm bin/console app:update --no-interaction ``` +## Container images + +Production deployments run two images: + +- `ghcr.io/os2display/display-api-service` — the php-fpm application +- `ghcr.io/os2display/display-api-service-nginx` — the nginx reverse-proxy serving static files and forwarding + PHP requests + +Both are built and published from this repository. See [`infrastructure/Readme.md`](infrastructure/Readme.md) +for the build pipeline (stages, tag scheme, local + CI flows). + +### Changing environment variables for the running images + +Set runtime configuration via your container runtime, not by editing the `.env` files baked into the image: + +- Docker Compose: `env_file:` or `environment:` on the `os2display` service. +- Other orchestrators: equivalent native mechanism (`-e`, env injection, etc.). + +Real environment variables take precedence over the image's compiled `.env.local.php`, so values set this way +override the committed `.env` baselines. Restart the container after changing them — Symfony reads its +configuration once at boot. + ## Coding standards Before a PR can be merged it has to pass the GitHub Actions checks. See `.github/workflows` for workflows that should diff --git a/infrastructure/Readme.md b/infrastructure/Readme.md index 604f59ee6..948f80de5 100644 --- a/infrastructure/Readme.md +++ b/infrastructure/Readme.md @@ -18,22 +18,51 @@ Images are published to . ## Building images locally -`infrastructure/build.sh` builds both images. By default it builds for the host platform and loads the result -into the local docker daemon. Set `PUSH=1` to build multi-arch and push to the registry — opting in keeps a bare -`sh infrastructure/build.sh` from accidentally publishing. +Two Taskfile entries wrap the build script: ```sh -sh infrastructure/build.sh # build + load locally -PUSH=1 sh infrastructure/build.sh # build multi-arch + push to GHCR +task images:build # build + load into the local docker daemon (host platform) +task images:push # build multi-arch + push to GHCR (prompts for confirmation) ``` +`task images:push` prompts before publishing because the action is non-reversible. The underlying script is +`infrastructure/build.sh`; running it directly defaults to local load. Set `PUSH=1` to push. + ## Build process -The API (php-fpm) image is built first via a multistage build that performs the `npm` and `composer` build stages -and bakes the result into `/var/www/html`. The nginx image then layers on top of the published API image as a -`FROM` stage and copies the `public/` tree out of it. There is no duplicated builder logic — the `npm`/`composer` -work runs once per CI run. +The two images are built sequentially in the same job; the nginx image layers on top of the just-published API +image as a `FROM` stage. The full pipeline: + +1. **Build the assets stage (`assets_builder`).** Runs `npm ci` against the lockfile, then `npm run build`. + Vite emits the bundled JS/CSS to `/app/public/build/`. + *Why:* the API image needs the vite manifest in place before its `composer install` triggers + `cache:clear` — the symfony-vite bundle generates cache config files from the manifest at that point. + +2. **Build the API stage (`api_app_builder`).** Two-pass composer install: + 1. `composer install --no-dev --no-scripts` against `composer.lock` only — populates `vendor/` in a layer + that **stays cached as long as the lockfile is unchanged**, even if all the source code changes. + 2. After the full source tree and the vite manifest are copied in, `composer install` runs again with + scripts so Symfony's auto-scripts (`cache:clear`, `assets:install`) finalise the autoloader and config + cache. + + `release.json` is written into `public/` after the second install, with the `APP_VERSION`, + `APP_RELEASE_TIMESTAMP`, and `APP_RELEASE_TIME` build args. + *Why:* the screen client polls `/release.json` to detect when it should refresh. + +3. **Assemble the production API image.** Copies `vendor/`+source from `api_app_builder`, drops in `composer` + (needed at container start by the entrypoint to `composer dump-env prod`), the Prometheus php-fpm exporter, + and `docker-entrypoint.sh`. `ENTRYPOINT` runs `dump-env` + `cache:warmup` against the operator's runtime env + before exec'ing `CMD ["php-fpm"]`. + +4. **Push the API image.** `task images:push` (or the workflow) pushes the multi-arch manifest to + `ghcr.io/os2display/display-api-service:` *before* the nginx stage runs. + *Why:* the nginx image's `FROM` references this published image — multi-arch manifests resolve per + platform, so the arm64 nginx build copies arm64 contents and amd64 copies amd64 without QEMU re-execution + of npm/composer. + +5. **Build and push the nginx image.** Layers on `ghcr.io/os2display/display-api-service:` as the `app` + stage, `COPY`s `/var/www/html/public/` out of it, drops in the nginx config, and exposes a `/health` + endpoint. Pushed to `ghcr.io/os2display/display-api-service-nginx:`. -Because the nginx build depends on the API image, the workflow builds and pushes the API image first; the nginx -step pulls the just-published manifest. For local builds, `build.sh` runs the same order, so the API image is -loaded into the local daemon under the same tag and BuildKit reuses it without a network pull. +For local `task images:build`, steps 4 and 5 run against the local docker daemon instead of the registry — +BuildKit reuses the locally-loaded API image for nginx's `FROM` without a network pull. diff --git a/infrastructure/display-api-service/Dockerfile b/infrastructure/display-api-service/Dockerfile index 148f0fa7c..32632aa55 100644 --- a/infrastructure/display-api-service/Dockerfile +++ b/infrastructure/display-api-service/Dockerfile @@ -14,9 +14,12 @@ COPY --from=repository-root package.json package-lock.json vite.config.js ./ RUN npm ci --no-audit --no-fund # Source for `npm run build`. Vite emits to /app/public/build, which the -# api_app_builder stage copies out. +# api_app_builder stage copies out. Files outside of `assets/` (the vite +# entrypoints) are not consumed by the build and don't belong here — +# `public/client/online-check/` and the `public/media/thumbnail_*.png` +# fallbacks reach the final image via the api_app_builder's repo-root +# copy below. COPY --from=repository-root assets/ ./assets/ -COPY --from=repository-root public/client/ ./public/client/ RUN npm run build @@ -54,6 +57,10 @@ USER deploy COPY --chown=deploy:deploy --from=repository-root composer.json composer.lock symfony.lock ./ RUN APP_ENV=prod composer install --no-dev -o --classmap-authoritative --no-scripts +# Brings in the full source tree, including the static assets the screen +# client needs at runtime (public/client/online-check/, the +# public/media/thumbnail_*.png fallbacks). .dockerignore filters out the +# rest of public/media/ — see the file for the preserved entries. COPY --chown=deploy:deploy --from=repository-root ./ ./ # Vite manifest must be in place before the second composer install From 74196f100ed1f3d0e10ef941ffc91f34fc7db5a4 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Fri, 1 May 2026 17:16:10 +0200 Subject: [PATCH 56/59] Sanitise specs in api-spec workflow before oasdiff MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit API Platform 3.4 emits empty header maps as YAML sequences (`headers: []`), which oasdiff (strict OpenAPI 3.x parser) refuses to load — every breaking-changes job since the workflow rework has been falsely tripping the "breaking changes detected" branch with no diff to show, because oasdiff aborts before comparing anything. Workaround: materialise both base and revision specs into .api-spec-tmp/ and rewrite `headers: []` to the spec-compliant `headers: {}` before invoking oasdiff. Drop on upgrade to API Platform 4.x — its exporter serialises empty maps correctly. TODO marker in the workflow points there. Verified: 31 malformed entries on the current spec, 0 after sed, resulting YAML still parses. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/apispec.yaml | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/.github/workflows/apispec.yaml b/.github/workflows/apispec.yaml index abfd6c496..e22bda4e3 100644 --- a/.github/workflows/apispec.yaml +++ b/.github/workflows/apispec.yaml @@ -101,13 +101,32 @@ jobs: - name: Fetch base branch for comparison run: git fetch --depth=1 origin ${{ github.base_ref }} + # API Platform 3.4 emits empty header maps as YAML sequences + # (`headers: []`) — out of spec per OpenAPI 3.x, which mandates a map. + # oasdiff is strict and refuses to load either side, so we materialise + # both specs and rewrite to the canonical empty-map form before + # diffing. Files live inside the workspace so the oasdiff-action's + # docker container (which mounts the repo as /github/workspace) can + # read them. + # TODO: drop this sanitisation once we upgrade to API Platform 4.x — + # the exporter there serialises empty maps correctly. Track upgrade + # in https://github.com/os2display/display-api-service/issues (open + # an issue when the upgrade is scheduled). + - name: Sanitise specs (`headers: []` → `headers: {}`) + run: | + mkdir -p .api-spec-tmp + git show "origin/${{ github.base_ref }}:public/api-spec-v2.yaml" \ + | sed 's/headers: \[\]/headers: {}/g' > .api-spec-tmp/base.yaml + sed 's/headers: \[\]/headers: {}/g' public/api-spec-v2.yaml \ + > .api-spec-tmp/revision.yaml + - name: Detect breaking changes id: breaking continue-on-error: true uses: oasdiff/oasdiff-action/breaking@v0.0.44 with: - base: "origin/${{ github.base_ref }}:public/api-spec-v2.yaml" - revision: "public/api-spec-v2.yaml" + base: .api-spec-tmp/base.yaml + revision: .api-spec-tmp/revision.yaml fail-on: ERR - name: Generate changelog @@ -115,8 +134,8 @@ jobs: continue-on-error: true uses: oasdiff/oasdiff-action/changelog@v0.0.44 with: - base: "origin/${{ github.base_ref }}:public/api-spec-v2.yaml" - revision: "public/api-spec-v2.yaml" + base: .api-spec-tmp/base.yaml + revision: .api-spec-tmp/revision.yaml format: markdown output-to-file: changelog.md From a4908793d275ea82831d06fc22f2542d0be7accb Mon Sep 17 00:00:00 2001 From: turegjorup Date: Fri, 1 May 2026 17:19:30 +0200 Subject: [PATCH 57/59] Quote sanitise-step name to keep YAML parser happy Prettier flagged the unquoted `headers: []` inside the step name as a nested mapping. Quoting the whole name turns it back into a plain scalar. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/apispec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/apispec.yaml b/.github/workflows/apispec.yaml index e22bda4e3..e5887e76d 100644 --- a/.github/workflows/apispec.yaml +++ b/.github/workflows/apispec.yaml @@ -112,7 +112,7 @@ jobs: # the exporter there serialises empty maps correctly. Track upgrade # in https://github.com/os2display/display-api-service/issues (open # an issue when the upgrade is scheduled). - - name: Sanitise specs (`headers: []` → `headers: {}`) + - name: "Sanitise specs (`headers: []` → `headers: {}`)" run: | mkdir -p .api-spec-tmp git show "origin/${{ github.base_ref }}:public/api-spec-v2.yaml" \ From 6376ca07ed9da307a9dc435fe667e6c3eb07e0bc Mon Sep 17 00:00:00 2001 From: turegjorup Date: Fri, 1 May 2026 17:38:38 +0200 Subject: [PATCH 58/59] Make api-spec comment update in place across runs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Track the workflow's prior comment by an invisible HTML marker () so subsequent runs update the same comment in place rather than leaving stale ones — and so we can distinguish this workflow's comment from any other bot comments on the PR. Three branches now write the marker plus content: - non-breaking changes (changelog populated, oasdiff success) - breaking changes (oasdiff failure) - resolved (no diff this run, but a prior comment exists) — only fires when there's something to clear, so a clean PR with no prior comment still produces no noise. Each branch updates the prior comment by id when found, posts a fresh comment otherwise. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/apispec.yaml | 59 +++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 8 deletions(-) diff --git a/.github/workflows/apispec.yaml b/.github/workflows/apispec.yaml index e5887e76d..a56f06650 100644 --- a/.github/workflows/apispec.yaml +++ b/.github/workflows/apispec.yaml @@ -139,29 +139,49 @@ jobs: format: markdown output-to-file: changelog.md - # Stay quiet when nothing changed — neither breaking nor non-breaking. - # Comments only fire on the two branches below. + # Identify any prior comment from this workflow by an invisible HTML + # marker, so subsequent runs update the same comment in place even + # when other GitHub-Actions bot comments exist on the PR. + - name: Find previous workflow comment + id: prev-comment + env: + GH_TOKEN: ${{ github.token }} + run: | + id=$(gh api \ + "repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" \ + --paginate \ + --jq '.[] | select(.body | startswith("")) | .id' \ + | tail -n1) + echo "id=${id:-}" >> "$GITHUB_OUTPUT" - name: Comment PR - non-breaking changes if: steps.breaking.outcome == 'success' && hashFiles('changelog.md') != '' env: GH_TOKEN: ${{ github.token }} + PREV_ID: ${{ steps.prev-comment.outputs.id }} run: | { + echo "" echo "## API Specification - Non-breaking changes" echo "" cat changelog.md } > comment.md - gh pr comment ${{ github.event.pull_request.number }} \ - --body-file comment.md \ - --create-if-none --edit-last + if [ -n "$PREV_ID" ]; then + jq -Rs '{body: .}' < comment.md \ + | gh api "repos/${{ github.repository }}/issues/comments/$PREV_ID" \ + --method PATCH --input - + else + gh pr comment ${{ github.event.pull_request.number }} --body-file comment.md + fi - name: Comment PR - breaking changes if: steps.breaking.outcome == 'failure' env: GH_TOKEN: ${{ github.token }} + PREV_ID: ${{ steps.prev-comment.outputs.id }} run: | { + echo "" echo "## API Specification - Breaking changes detected" echo "" if [ -s changelog.md ]; then @@ -170,9 +190,32 @@ jobs: echo "The breaking changes action detected incompatible changes. Review the action logs for details." fi } > comment.md - gh pr comment ${{ github.event.pull_request.number }} \ - --body-file comment.md \ - --create-if-none --edit-last + if [ -n "$PREV_ID" ]; then + jq -Rs '{body: .}' < comment.md \ + | gh api "repos/${{ github.repository }}/issues/comments/$PREV_ID" \ + --method PATCH --input - + else + gh pr comment ${{ github.event.pull_request.number }} --body-file comment.md + fi + + # If a prior run posted a comment but this run is clean, update the + # existing comment to reflect that. Don't post anything when there + # was no prior comment — that would be the noise we just removed. + - name: Mark previous comment resolved (when no changes) + if: steps.breaking.outcome == 'success' && hashFiles('changelog.md') == '' && steps.prev-comment.outputs.id != '' + env: + GH_TOKEN: ${{ github.token }} + PREV_ID: ${{ steps.prev-comment.outputs.id }} + run: | + { + echo "" + echo "## API Specification" + echo "" + echo "_No changes detected on this run — previous diff resolved._" + } > comment.md + jq -Rs '{body: .}' < comment.md \ + | gh api "repos/${{ github.repository }}/issues/comments/$PREV_ID" \ + --method PATCH --input - - name: Fail if breaking changes detected if: steps.breaking.outcome == 'failure' From 95e663af8434b014a02e125e5265b553e2d52f14 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Fri, 1 May 2026 17:43:46 +0200 Subject: [PATCH 59/59] Detect oasdiff "no changes" placeholder before commenting `hashFiles('changelog.md') != ''` was matching the file even when oasdiff's `changelog` action wrote "# API Changelog 1 vs. 1\nNo changes detected" to it (when the diff was empty), so the workflow posted a "Non-breaking changes" comment whose body explicitly said nothing changed. Add a `Detect actual diff in changelog` step that greps for the "No changes detected" placeholder and exposes a boolean output; gate the non-breaking branch on actual changes, and the resolved branch on no changes. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/apispec.yaml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/apispec.yaml b/.github/workflows/apispec.yaml index a56f06650..94855d683 100644 --- a/.github/workflows/apispec.yaml +++ b/.github/workflows/apispec.yaml @@ -139,6 +139,18 @@ jobs: format: markdown output-to-file: changelog.md + # oasdiff's changelog action writes a "No changes detected" placeholder + # into changelog.md even when there's no diff, so file existence isn't + # a reliable signal — grep the body for the placeholder string instead. + - name: Detect actual diff in changelog + id: changes + run: | + if [ -s changelog.md ] && ! grep -q 'No changes detected' changelog.md; then + echo "has_changes=true" >> "$GITHUB_OUTPUT" + else + echo "has_changes=false" >> "$GITHUB_OUTPUT" + fi + # Identify any prior comment from this workflow by an invisible HTML # marker, so subsequent runs update the same comment in place even # when other GitHub-Actions bot comments exist on the PR. @@ -155,7 +167,7 @@ jobs: echo "id=${id:-}" >> "$GITHUB_OUTPUT" - name: Comment PR - non-breaking changes - if: steps.breaking.outcome == 'success' && hashFiles('changelog.md') != '' + if: steps.breaking.outcome == 'success' && steps.changes.outputs.has_changes == 'true' env: GH_TOKEN: ${{ github.token }} PREV_ID: ${{ steps.prev-comment.outputs.id }} @@ -202,7 +214,7 @@ jobs: # existing comment to reflect that. Don't post anything when there # was no prior comment — that would be the noise we just removed. - name: Mark previous comment resolved (when no changes) - if: steps.breaking.outcome == 'success' && hashFiles('changelog.md') == '' && steps.prev-comment.outputs.id != '' + if: steps.breaking.outcome == 'success' && steps.changes.outputs.has_changes == 'false' && steps.prev-comment.outputs.id != '' env: GH_TOKEN: ${{ github.token }} PREV_ID: ${{ steps.prev-comment.outputs.id }}