diff --git a/Justfile b/Justfile index 1131bde2c6..a7b62a811c 100644 --- a/Justfile +++ b/Justfile @@ -86,6 +86,18 @@ phar: wasm: && phar cd wasm && ./build.sh +# Idempotent — both tools recompress losslessly, so it is safe to re-run over the whole folder. +# Requires nix-shell (oxipng/jpegoptim). +# Losslessly optimize landing-site images in place: oxipng for PNG, jpegoptim for JPEG. +optimize-images: + #!/usr/bin/env bash + set -euo pipefail + images_dir="web/landing/assets/images" + oxipng --opt max --strip safe --recursive "$images_dir" + while IFS= read -r -d '' f; do + jpegoptim --strip-all --all-progressive "$f" + done < <(find "$images_dir" -type f \( -iname '*.jpg' -o -iname '*.jpeg' \) -print0) + # Build the Docker image. docker: docker buildx build -t flow-php/flow:latest . --progress=plain --load diff --git a/shell.nix b/shell.nix index db846ed57e..5d865d4f3b 100644 --- a/shell.nix +++ b/shell.nix @@ -81,6 +81,8 @@ pkgs.mkShell { pkgs.actionlint pkgs.zizmor pkgs.just + pkgs.oxipng + pkgs.jpegoptim ] ++ pkgs.lib.optional with-blackfire pkgs.blackfire ++ pkgs.lib.optionals with-wasm [ diff --git a/web/landing/assets/codemirror/completions/dsl.js b/web/landing/assets/codemirror/completions/dsl.js index 8d86171248..ea762d5c7d 100644 --- a/web/landing/assets/codemirror/completions/dsl.js +++ b/web/landing/assets/codemirror/completions/dsl.js @@ -1,7 +1,7 @@ /** * CodeMirror Completer for Flow PHP DSL Functions * - * Total functions: 783 + * Total functions: 790 * * This completer provides autocompletion for all Flow PHP DSL functions: * - Extractors (flow-extractors) @@ -2830,15 +2830,15 @@ const dslFunctions = [ const div = document.createElement("div") div.innerHTML = `
- context(TraceId $traceId = null, Baggage $baggage = null) : Context + context(Baggage $baggage = null) : Context
- Create a Context.
If no TraceId is provided, generates a new one.
If no Baggage is provided, creates an empty one.
@param null|TraceId $traceId Optional TraceId to use
@param null|Baggage $baggage Optional Baggage to use + Create a root Context (no active span).
A span created in this context becomes a new trace root. Attach an active span with
Context::withActiveSpan() to make subsequent spans its children.
@param null|Baggage $baggage Optional Baggage to use
` return div }, - apply: snippet("\\Flow\\Telemetry\\DSL\\context(" + "$" + "{" + "1:traceId" + "}" + ", " + "$" + "{" + "2:baggage" + "}" + ")"), + apply: snippet("\\Flow\\Telemetry\\DSL\\context(" + "$" + "{" + "1:baggage" + "}" + ")"), boost: 10 }, { label: "copy", @@ -4066,15 +4066,15 @@ const dslFunctions = [ const div = document.createElement("div") div.innerHTML = `
- filesystem_telemetry_options(bool $traceStreams = true, bool $collectMetrics = true) : FilesystemTelemetryOptions + filesystem_telemetry_options(bool $trace_streams = true, bool $collect_metrics = true) : FilesystemTelemetryOptions
- Create options for filesystem telemetry.
@param bool $traceStreams Create a single span per stream lifecycle (default: ON)
@param bool $collectMetrics Collect metrics for bytes/operation counts (default: ON) + Create options for filesystem telemetry.
@param bool $trace_streams Create a single span per stream lifecycle (default: ON)
@param bool $collect_metrics Collect metrics for bytes/operation counts (default: ON)
` return div }, - apply: snippet("\\Flow\\Filesystem\\DSL\\filesystem_telemetry_options(" + "$" + "{" + "1:traceStreams" + "}" + ", " + "$" + "{" + "2:collectMetrics" + "}" + ")"), + apply: snippet("\\Flow\\Filesystem\\DSL\\filesystem_telemetry_options(" + "$" + "{" + "1:trace_streams" + "}" + ", " + "$" + "{" + "2:collect_metrics" + "}" + ")"), boost: 10 }, { label: "file_copy", @@ -4919,6 +4919,24 @@ const dslFunctions = [ }, apply: snippet("\\Flow\\Types\\DSL\\get_type(" + "$" + "{" + "1:value" + "}" + ")"), boost: 10 + }, { + label: "git_detector", + type: "function", + detail: "flow\u002Ddsl\u002Dhelpers", + info: () => { + const div = document.createElement("div") + div.innerHTML = ` +
+ git_detector(string $workingDirectory = null, string $gitBinary = 'git') : GitDetector +
+
+ Create a GitDetector.
@param null|string $workingDirectory Directory to run git in (default: current working directory)
@param string $gitBinary Path to the git binary (default: \"git\", resolved from $PATH) +
+ ` + return div + }, + apply: snippet("\\Flow\\Telemetry\\DSL\\git_detector(" + "$" + "{" + "1:workingDirectory" + "}" + ", " + "$" + "{" + "2:gitBinary" + "}" + ")"), + boost: 10 }, { label: "grant", type: "function", @@ -6848,6 +6866,42 @@ const dslFunctions = [ }, apply: snippet("\\Flow\\Telemetry\\DSL\\os_detector()"), boost: 10 + }, { + label: "otlp_async_curl_options", + type: "function", + detail: "flow\u002Ddsl\u002Dhelpers", + info: () => { + const div = document.createElement("div") + div.innerHTML = ` +
+ otlp_async_curl_options() : AsyncCurlTransportOptions +
+
+ Create async curl transport options for OTLP. +
+ ` + return div + }, + apply: snippet("\\Flow\\Bridge\\Telemetry\\OTLP\\DSL\\otlp_async_curl_options()"), + boost: 10 + }, { + label: "otlp_async_curl_transport", + type: "function", + detail: "flow\u002Ddsl\u002Dhelpers", + info: () => { + const div = document.createElement("div") + div.innerHTML = ` +
+ otlp_async_curl_transport(string $endpoint, JsonSerializer|ProtobufSerializer $serializer = Flow\\Bridge\\Telemetry\\OTLP\\Serializer\\JsonSerializer::..., AsyncCurlTransportOptions $options = Flow\\Bridge\\Telemetry\\OTLP\\Transport\\AsyncCurlTransportOptions::..., Transport $failover = null, ErrorHandler $error_handler = Flow\\Telemetry\\ErrorHandler\\ErrorLogHandler::...) : Transport +
+
+ Create an asynchronous curl transport for OTLP endpoints.
@param string $endpoint OTLP endpoint URL (e.g., \'http://localhost:4318\')
@param JsonSerializer|ProtobufSerializer $serializer Serializer for encoding telemetry data (JSON or Protobuf)
@param AsyncCurlTransportOptions $options Transport configuration options
@param ?Transport $failover Optional failover transport receiving prior batches when primary fails
@param ErrorHandler $error_handler Handler for failures reaped on send()/tick()/shutdown() (no failover) +
+ ` + return div + }, + apply: snippet("\\Flow\\Bridge\\Telemetry\\OTLP\\DSL\\otlp_async_curl_transport(" + "$" + "{" + "1:endpoint" + "}" + ", " + "$" + "{" + "2:serializer" + "}" + ", " + "$" + "{" + "3:options" + "}" + ", " + "$" + "{" + "4:failover" + "}" + ", " + "$" + "{" + "5:error_handler" + "}" + ")"), + boost: 10 }, { label: "otlp_curl_options", type: "function", @@ -6877,7 +6931,7 @@ const dslFunctions = [ otlp_curl_transport(string $endpoint, JsonSerializer|ProtobufSerializer $serializer = Flow\\Bridge\\Telemetry\\OTLP\\Serializer\\JsonSerializer::..., CurlTransportOptions $options = Flow\\Bridge\\Telemetry\\OTLP\\Transport\\CurlTransportOptions::..., Transport $failover = null) : Transport
- Create an async curl transport for OTLP endpoints.
Creates a CurlTransport that uses curl_multi for non-blocking I/O.
Requests are queued and executed asynchronously. OTLP/HTTP allows JSON
or Protobuf encoding; defaults to JSON.
Requires: ext-curl PHP extension
@param string $endpoint OTLP endpoint URL (e.g., \'http://localhost:4318\')
@param JsonSerializer|ProtobufSerializer $serializer Serializer for encoding telemetry data (JSON or Protobuf)
@param CurlTransportOptions $options Transport configuration options
@param ?Transport $failover Optional failover transport receiving prior batches when primary fails + Create a synchronous curl transport for OTLP endpoints.
Creates a CurlTransport that drives each request to completion and reports the
outcome immediately (returns on success, throws on failure). OTLP/HTTP allows
JSON or Protobuf encoding; defaults to JSON. Keeping export off the application
hot path is the job of the batching processor in front of the exporter.
Requires: ext-curl PHP extension
@param string $endpoint OTLP endpoint URL (e.g., \'http://localhost:4318\')
@param JsonSerializer|ProtobufSerializer $serializer Serializer for encoding telemetry data (JSON or Protobuf)
@param CurlTransportOptions $options Transport configuration options
@param ?Transport $failover Optional failover transport receiving the batch when the primary fails
` return div @@ -9128,6 +9182,72 @@ const dslFunctions = [ }, apply: snippet("\\Flow\\PostgreSql\\DSL\\schema_sequence(" + "$" + "{" + "1:name" + "}" + ", " + "$" + "{" + "2:dataType" + "}" + ", " + "$" + "{" + "3:startValue" + "}" + ", " + "$" + "{" + "4:minValue" + "}" + ", " + "$" + "{" + "5:maxValue" + "}" + ", " + "$" + "{" + "6:incrementBy" + "}" + ", " + "$" + "{" + "7:cycle" + "}" + ", " + "$" + "{" + "8:cacheValue" + "}" + ", " + "$" + "{" + "9:ownedByTable" + "}" + ", " + "$" + "{" + "10:ownedByColumn" + "}" + ")"), boost: 10 + }, { + label: "schema_sort_by_metadata", + type: "function", + detail: "flow\u002Ddsl\u002Dschema", + info: () => { + const div = document.createElement("div") + div.innerHTML = ` +
+ schema_sort_by_metadata(string $key, SortOrder $order = Flow\\ETL\\Row\\SortOrder::...) : SortingStrategy +
+ ` + return div + }, + apply: snippet("\\Flow\\ETL\\DSL\\schema_sort_by_metadata(" + "$" + "{" + "1:key" + "}" + ", " + "$" + "{" + "2:order" + "}" + ")"), + boost: 10 + }, { + label: "schema_sort_by_name", + type: "function", + detail: "flow\u002Ddsl\u002Dschema", + info: () => { + const div = document.createElement("div") + div.innerHTML = ` +
+ schema_sort_by_name(SortOrder $order = Flow\\ETL\\Row\\SortOrder::...) : SortingStrategy +
+ ` + return div + }, + apply: snippet("\\Flow\\ETL\\DSL\\schema_sort_by_name(" + "$" + "{" + "1:order" + "}" + ")"), + boost: 10 + }, { + label: "schema_sort_by_type", + type: "function", + detail: "flow\u002Ddsl\u002Dschema", + info: () => { + const div = document.createElement("div") + div.innerHTML = ` +
+ schema_sort_by_type(array $priorities = [...], SortOrder $order = Flow\\ETL\\Row\\SortOrder::...) : SortingStrategy +
+
+ @param array>, int> $priorities +
+ ` + return div + }, + apply: snippet("\\Flow\\ETL\\DSL\\schema_sort_by_type(" + "$" + "{" + "1:priorities" + "}" + ", " + "$" + "{" + "2:order" + "}" + ")"), + boost: 10 + }, { + label: "schema_sort_by_type_and_name", + type: "function", + detail: "flow\u002Ddsl\u002Dschema", + info: () => { + const div = document.createElement("div") + div.innerHTML = ` +
+ schema_sort_by_type_and_name(array $priorities = [...], SortOrder $order = Flow\\ETL\\Row\\SortOrder::...) : SortingStrategy +
+
+ @param array>, int> $priorities +
+ ` + return div + }, + apply: snippet("\\Flow\\ETL\\DSL\\schema_sort_by_type_and_name(" + "$" + "{" + "1:priorities" + "}" + ", " + "$" + "{" + "2:order" + "}" + ")"), + boost: 10 }, { label: "schema_strict_validator", type: "function", diff --git a/web/landing/assets/images/banner.png b/web/landing/assets/images/banner.png index 32fc4994ac..ba05d7c6de 100644 Binary files a/web/landing/assets/images/banner.png and b/web/landing/assets/images/banner.png differ diff --git a/web/landing/assets/images/blog/.DS_Store b/web/landing/assets/images/blog/.DS_Store new file mode 100644 index 0000000000..06cfb14c6a Binary files /dev/null and b/web/landing/assets/images/blog/.DS_Store differ diff --git a/web/landing/assets/images/blog/flow-php-release-0410/messenger_link.png b/web/landing/assets/images/blog/flow-php-release-0410/messenger_link.png new file mode 100644 index 0000000000..8a000938e8 Binary files /dev/null and b/web/landing/assets/images/blog/flow-php-release-0410/messenger_link.png differ diff --git a/web/landing/assets/images/blog/flow-php-release-0410/profiler_01.png b/web/landing/assets/images/blog/flow-php-release-0410/profiler_01.png new file mode 100644 index 0000000000..58b1d04b1c Binary files /dev/null and b/web/landing/assets/images/blog/flow-php-release-0410/profiler_01.png differ diff --git a/web/landing/assets/images/blog/flow-php-release-0410/profiler_02.png b/web/landing/assets/images/blog/flow-php-release-0410/profiler_02.png new file mode 100644 index 0000000000..599d22a13d Binary files /dev/null and b/web/landing/assets/images/blog/flow-php-release-0410/profiler_02.png differ diff --git a/web/landing/assets/images/blog/flow-php-release-0410/profiler_migrations.png b/web/landing/assets/images/blog/flow-php-release-0410/profiler_migrations.png new file mode 100644 index 0000000000..1aad168a68 Binary files /dev/null and b/web/landing/assets/images/blog/flow-php-release-0410/profiler_migrations.png differ diff --git a/web/landing/assets/images/blog/flow-php-release-0410/profiler_queries.png b/web/landing/assets/images/blog/flow-php-release-0410/profiler_queries.png new file mode 100644 index 0000000000..b439f14cb3 Binary files /dev/null and b/web/landing/assets/images/blog/flow-php-release-0410/profiler_queries.png differ diff --git a/web/landing/assets/images/favicons/apple-touch-icon.png b/web/landing/assets/images/favicons/apple-touch-icon.png index 5e4c2a1969..d6cba89d23 100644 Binary files a/web/landing/assets/images/favicons/apple-touch-icon.png and b/web/landing/assets/images/favicons/apple-touch-icon.png differ diff --git a/web/landing/assets/images/favicons/favicon-16x16.png b/web/landing/assets/images/favicons/favicon-16x16.png index aa799bbe25..4f5cf71001 100644 Binary files a/web/landing/assets/images/favicons/favicon-16x16.png and b/web/landing/assets/images/favicons/favicon-16x16.png differ diff --git a/web/landing/assets/images/favicons/favicon-32x32.png b/web/landing/assets/images/favicons/favicon-32x32.png index 107ca465b3..6febd3abe7 100644 Binary files a/web/landing/assets/images/favicons/favicon-32x32.png and b/web/landing/assets/images/favicons/favicon-32x32.png differ diff --git a/web/landing/assets/images/sponsors/1Password.png b/web/landing/assets/images/sponsors/1Password.png index 23e99f51bb..02796575dc 100644 Binary files a/web/landing/assets/images/sponsors/1Password.png and b/web/landing/assets/images/sponsors/1Password.png differ diff --git a/web/landing/assets/images/sponsors/cloudflare.png b/web/landing/assets/images/sponsors/cloudflare.png index 4d27bb3ce1..d0711b1903 100644 Binary files a/web/landing/assets/images/sponsors/cloudflare.png and b/web/landing/assets/images/sponsors/cloudflare.png differ diff --git a/web/landing/assets/images/sponsors/datadog.png b/web/landing/assets/images/sponsors/datadog.png index ffafa460a6..53f5458912 100644 Binary files a/web/landing/assets/images/sponsors/datadog.png and b/web/landing/assets/images/sponsors/datadog.png differ diff --git a/web/landing/assets/images/sponsors/tailscale.png b/web/landing/assets/images/sponsors/tailscale.png index 634db00d99..cca54fd14d 100644 Binary files a/web/landing/assets/images/sponsors/tailscale.png and b/web/landing/assets/images/sponsors/tailscale.png differ diff --git a/web/landing/assets/styles/app.css b/web/landing/assets/styles/app.css index b3dc7a2f09..d17bc1c0b8 100644 --- a/web/landing/assets/styles/app.css +++ b/web/landing/assets/styles/app.css @@ -117,9 +117,9 @@ code { font-size: 0.9em; } -/* Blog post prose. Wraps full-width with comfortable reading measure. */ +/* Blog post prose. Wraps full-width to match the site container. */ #blog-post { - @apply pb-5 px-4 sm:px-6 lg:px-8 mx-auto max-w-3xl text-slate-700 dark:text-white/85; + @apply pb-5 px-4 sm:px-6 lg:px-8 mx-auto max-w-screen-2xl text-slate-700 dark:text-white/85; } #blog-post a { @@ -190,6 +190,18 @@ code { @apply border-b border-slate-200 px-3 py-2 dark:border-white/10; } +#blog-post img { + @apply max-w-full h-auto rounded-lg my-6 mx-auto; +} + +#blog-post figure { + @apply my-8; +} + +#blog-post figcaption { + @apply text-sm text-slate-500 text-center mt-2 dark:text-white/55; +} + #example-description h1 { @apply font-bold text-2xl; diff --git a/web/landing/src/Flow/Website/Blog/Posts.php b/web/landing/src/Flow/Website/Blog/Posts.php index 0ad9776303..a9428f1f50 100644 --- a/web/landing/src/Flow/Website/Blog/Posts.php +++ b/web/landing/src/Flow/Website/Blog/Posts.php @@ -39,6 +39,12 @@ final class Posts 'date' => '2025-03-16', 'slug' => 'flow-php-release-cycle', ], + [ + 'title' => 'Flow PHP - Release 0.41.0', + 'description' => 'Summary of things changed in Flow PHP 0.41.0 release', + 'date' => '2026-06-29', + 'slug' => 'flow-php-release-0410', + ], ]; /** diff --git a/web/landing/templates/blog/posts/2026-06-29/flow-php-release-0410/exporter_enabling.yaml b/web/landing/templates/blog/posts/2026-06-29/flow-php-release-0410/exporter_enabling.yaml new file mode 100644 index 0000000000..4f849c0c2a --- /dev/null +++ b/web/landing/templates/blog/posts/2026-06-29/flow-php-release-0410/exporter_enabling.yaml @@ -0,0 +1,9 @@ +flow_telemetry: + exporters: + otlp_exporter: + enabled: '%env(bool:OTEL_ENABLED)%' # off → discard exports; profiler still captures every signal + otlp: + transport: + type: curl + endpoint: '%env(OTEL_ENDPOINT)%' + encoding: protobuf \ No newline at end of file diff --git a/web/landing/templates/blog/posts/2026-06-29/flow-php-release-0410/messenger_linking.yaml b/web/landing/templates/blog/posts/2026-06-29/flow-php-release-0410/messenger_linking.yaml new file mode 100644 index 0000000000..186f5f52bd --- /dev/null +++ b/web/landing/templates/blog/posts/2026-06-29/flow-php-release-0410/messenger_linking.yaml @@ -0,0 +1,7 @@ +flow_telemetry: + instrumentation: + messenger: + enabled: true + context_propagation: true # Propagate context across message boundaries + propagation_style: link # link|continue - How the consumer span relates to the producer span + link_to_worker: true # Link each message trace back to the messenger:consume worker span \ No newline at end of file diff --git a/web/landing/templates/blog/posts/2026-06-29/flow-php-release-0410/post.html.twig b/web/landing/templates/blog/posts/2026-06-29/flow-php-release-0410/post.html.twig new file mode 100644 index 0000000000..a2c8963553 --- /dev/null +++ b/web/landing/templates/blog/posts/2026-06-29/flow-php-release-0410/post.html.twig @@ -0,0 +1,102 @@ +{% extends 'blog/post.html.twig' %} +{% block article %} +
+

Flow PHP - Release 0.41.0

+
+

+ This is a first blog post about Flow Release, and 0.41.0 is bringing a lot of cool new features, mostly + around Telemetry and Symfony Integration. +

+
+ The plan is to write a blog post for each release, or at least try to. +
+ +

Symfony Telemetry Bundle

+
+

+ This is where most of our efforts were focused on, so lets start from the most exciting one. +

+

Telemetry integration with Profiler

+
+ Symfony profiler showing Flow telemetry panel + Symfony profiler showing Flow telemetry panel +
Telemetry panel in the Symfony profiler
+
+ +

+ This way during local development, we don't need to setup a full scale APM to quickly check if given part of + the system is correctly collecting telemetry signals. +

+

+ Profiler integration is configurable on the bundle level, you can read more about it in documentation. +

+

Telemetry Worker Mode Support

+

+ Modern PHP systems are often deployed in the Worker mode, through runtimes like FrankenPHP or Swoole. + In order to support that, we introduced a ResettableContextStorage and a mechanism that can automatically detect if system is running in a worker mode. +

+

+ runtime_mode can also be configured at symfony configuration level. +

+
{% apply escape %}{% include template_folder ~ '/runtime_mode.yaml' %}{% endapply %}
+ +

Messenger traces linking

+

+ Symfony Telemetry Bundle can now link traces that published message on the queue with message handling process (trace). +

+
+ Link between symfony messenger traces +
Symfony Messenger traces linking
+
+

+ As we can see on the screenshot above, trace representing processing CreateOrderMessage + is now linked into two other Traces. +

+

+ The first one is a Worker trace (Flow Telemetry separates workers from handlers) and the second one is a + link to a Controller that published that CreateOrderMessage on the queue. +

+

+ That behavior is also configurable at Symfony Bundle level. +

+
{% apply escape %}{% include template_folder ~ '/messenger_linking.yaml' %}{% endapply %}
+

Disable/Enable Exporters

+

+ Integrating Telemetry into existing systems can be really challenging task. It might be a good idea to start + in development environment or staging first, and only when everything is calibrated and tuned, enable it also + at production. +

+

+ Now it can be achieved by a dedicated enabled option added to each export +

+
{% apply escape %}{% include template_folder ~ '/exporter_enabling.yaml' %}{% endapply %}
+

Symfony PostgreSQL Bundle - Profiler Integration

+

+ Similarly to Telemetry, PostgreSQL Bundle supports Symfony Profiler where we can see all queries and current migrations status. +

+
+ Symfony Profiler - Migrations View +
Symfony Profiler - Migrations View
+
+
+ Symfony Profiler - Queries View +
Symfony Profiler - Queries View
+
+

Integration with SEAL

+

+ SEAL - Search Engine Abstraction Layer is now supported + by flow through dedicated flow-php/etl-adapter-seal +

+

+ This adapter was delivered by Aleksander. +

+

+ This opens a new set of features for Flow, making scalable indexing documents from various sources easier than ever.
+ Please look at the example of indexing a CSV document: +

+
{% apply escape %}{% include template_folder ~ '/seal.php' %}{% endapply %}
+

+ Those are just the most important additions to Flow PHP. For more details, check full at Changelog +

+
+{% endblock %} diff --git a/web/landing/templates/blog/posts/2026-06-29/flow-php-release-0410/runtime_mode.yaml b/web/landing/templates/blog/posts/2026-06-29/flow-php-release-0410/runtime_mode.yaml new file mode 100644 index 0000000000..a212987849 --- /dev/null +++ b/web/landing/templates/blog/posts/2026-06-29/flow-php-release-0410/runtime_mode.yaml @@ -0,0 +1,2 @@ +flow_telemetry: + runtime_mode: auto # auto|classic|worker \ No newline at end of file diff --git a/web/landing/templates/blog/posts/2026-06-29/flow-php-release-0410/seal.php b/web/landing/templates/blog/posts/2026-06-29/flow-php-release-0410/seal.php new file mode 100644 index 0000000000..1a51d94f73 --- /dev/null +++ b/web/landing/templates/blog/posts/2026-06-29/flow-php-release-0410/seal.php @@ -0,0 +1,30 @@ +createIndex('users'); + +data_frame() + ->read(from_csv(__DIR__ . '/users.csv')) + ->write(to_seal_upsert($engine, 'users')) + ->run(); \ No newline at end of file diff --git a/web/landing/templates/main/changelog.html.twig b/web/landing/templates/main/changelog.html.twig index 6b98730f38..b7a29ffa03 100644 --- a/web/landing/templates/main/changelog.html.twig +++ b/web/landing/templates/main/changelog.html.twig @@ -13,7 +13,7 @@ {%- block hero -%}{%- endblock -%} {% block main %} -
+
Releases @@ -21,7 +21,7 @@

Changelog

Every release of Flow PHP, in chronological order.

-
+
{{ changelog_markdown|markdown_to_html }}
{% endblock %}