Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,14 @@ install:
yarn install --immutable
bundle install
cd tools/frontmatter-validator && npm ci
cd tools/changelog-validator && npm ci

validate-frontmatters:
npm --prefix tools/frontmatter-validator run validate

validate-konnect-changelog:
npm --prefix tools/changelog-validator run validate

# Using local dependencies, starts a doc site instance on http://localhost:4000.
run: ruby-version-check validate-frontmatters
NODE_OPTIONS="--max_old_space_size=8192" yarn netlify dev --offline --skip-wait-port --internal-disable-edge-functions
Expand Down
8 changes: 8 additions & 0 deletions app/_data/changelogs/konnect/download-dashboards-as-pdf.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
title: "Download dashboards as PDF"
date: 2026-07-31
content: |
You can now download any dashboard as a PDF directly from the dashboard menu.
The exported PDF is print-friendly and contains a link back to the live dashboard,
making it easy to share insights with stakeholders, upstream teams, or leadership
who do not have a Konnect account.
url: https://releases.konghq.com/en/download-dashboards-as-pdf-Nq9NwICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
title: "Import dashboards from JSON in Konnect Analytics"
date: 2026-07-31
content: |
Importing a dashboard from JSON has always been possible through the API. It's now
in the UI too. From Create dashboard, choose Import JSON definition and upload the
file from your machine. Konnect validates the file and builds the dashboard for you.
url: https://releases.konghq.com/en/import-dashboards-from-json-in-konnect-analytics
24 changes: 24 additions & 0 deletions app/_data/schemas/changelog/konnect.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"$id": "schema:changelog:konnect",
"type": "object",
"additionalProperties": false,
"required": ["title", "date", "content"],
"properties": {
"title": {
"type": "string",
"minLength": 1
},
"date": {
"type": "string",
"format": "date"
},
"content": {
"type": "string",
"minLength": 1
},
"url": {
"type": "string",
"format": "uri"
}
}
}
11 changes: 11 additions & 0 deletions app/_includes/components/konnect_changelog.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{% for entry in changelog.entries %}
<h3 id="{{ entry.title | slugify }}">{{ entry.title }}</h3>
<p class="text-sm"><strong>{{ entry.date | date: "%B %-d, %Y" }}</strong></p>

{{ entry.content | markdownify }}

{% if entry.url %}
<p><a href="{{ entry.url }}">Learn more</a></p>
{% endif %}

{% endfor %}
13 changes: 13 additions & 0 deletions app/_includes/components/konnect_changelog.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{% for entry in changelog.entries %}
<item>
<title>{{ entry.title | xml_escape }}</title>
<pubDate>{{ entry.date | date_to_rfc822 }}</pubDate>
<description>{{ entry.content | markdownify | strip_html | normalize_whitespace | xml_escape }}</description>
{% if entry.url %}
<link>{{ entry.url | xml_escape }}</link>
<guid isPermaLink="true">{{ entry.url | xml_escape }}</guid>
{% else %}
<guid isPermaLink="false">{{ entry.title | slugify }}-{{ entry.date | date: "%Y-%m-%d" }}</guid>
{% endif %}
</item>
{% endfor %}
39 changes: 39 additions & 0 deletions app/_plugins/drops/konnect_changelog.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# frozen_string_literal: true

module Jekyll
module Drops
class KonnectChangelog < Liquid::Drop # rubocop:disable Style/Documentation
class Entry < Liquid::Drop # rubocop:disable Style/Documentation
attr_reader :title, :date, :content, :url

def initialize(title:, date:, content:, url: nil) # rubocop:disable Lint/MissingSuper
@title = title
@date = date
@content = content
@url = url
end
end

def initialize(site:) # rubocop:disable Lint/MissingSuper
@site = site
end

def entries
@entries ||= raw_entries.map do |data|
Entry.new(
title: data['title'],
date: data['date'],
content: data['content'],
url: data['url']
)
end.sort_by(&:date).reverse
end

private

def raw_entries
(@site.data.dig('changelogs', 'konnect') || {}).values
end
end
end
end
35 changes: 35 additions & 0 deletions app/_plugins/tags/konnect_changelog.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# frozen_string_literal: true

module Jekyll
class RenderKonnectChangelog < Liquid::Tag # rubocop:disable Style/Documentation
def initialize(tag_name, param, _tokens)
super

@param = param.strip
end

def render(context)
@context = context
@page = @context.environments.first['page']
site = context.registers[:site]
changelog = Drops::KonnectChangelog.new(site:)

context.stack do
context['changelog'] = changelog
Liquid::Template.parse(template, { line_numbers: true }).render(context)
end
end

private

def template
if @page['output_format'] == 'rss'
File.read(File.expand_path('app/_includes/components/konnect_changelog.xml'))
else
File.read(File.expand_path('app/_includes/components/konnect_changelog.html'))
end
end
end
end

Liquid::Template.register_tag('konnect_changelog', Jekyll::RenderKonnectChangelog)
17 changes: 17 additions & 0 deletions app/konnect-platform/changelog-feed.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
layout: null
llm: false
permalink: /konnect-platform/changelog/feed.xml
output_format: rss
products:
- konnect
---
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>{{site.konnect_short_name}} changelog</title>
<link>{{site.links.web}}/konnect-platform/changelog/</link>
<description>Changelog for {{site.konnect_short_name}}.</description>
{% konnect_changelog %}
</channel>
</rss>
27 changes: 27 additions & 0 deletions app/konnect-platform/changelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
title: "{{site.konnect_short_name}} changelog"
content_type: reference
layout: reference
products:
- konnect
works_on:
- konnect
breadcrumbs:
- /konnect/
description: "Changelog for {{site.konnect_short_name}}."

related_resources:
- text: "{{site.konnect_short_name}} changelog RSS feed"
url: https://developer.konghq.com/konnect-platform/changelog/feed.xml

tags:
- changelog

search_aliases:
- release notes
- konnect release notes
- whats new
---
Changelog for {{site.konnect_short_name}}.

{% konnect_changelog %}
29 changes: 29 additions & 0 deletions docs/konnect-changelog-entries.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Writing a Konnect changelog entry

Konnect's changelog is authored directly in this repo. Each entry is one YAML file — add yours in the same PR as the change it describes (or right before you ship it), and it publishes automatically on merge.

## Adding an entry

1. Create a new file in `app/_data/changelogs/konnect/`, named after your change, e.g. `download-dashboards-as-pdf.yml`.
2. Fill it in using this template:

```yaml
title: "Download dashboards as PDF"
date: 2026-07-31
content: |
You can now download any dashboard as a PDF directly from the dashboard menu.
The exported PDF is print-friendly and contains a link back to the live dashboard.
url: https://releases.konghq.com/en/download-dashboards-as-pdf-Nq9NwICE
```

| Field | Required | Notes |
|---|---|---|
| `title` | Yes | Short, user-facing summary of the change. |
| `date` | Yes | `YYYY-MM-DD`. |
| `content` | Yes | Full description. Markdown is fine. |
| `url` | No | Link to a demo, related doc, or the original announcement. |

3. Run `make validate-konnect-changelog` locally to catch typos or missing fields before opening your PR.
4. Open the PR. On merge, the entry renders at `/konnect-platform/changelog/` and in the RSS feed at `/konnect-platform/changelog/feed.xml`.

Do not add a file named `template.yml` (or anything else you don't want published) to `app/_data/changelogs/konnect/` — every file in that directory is loaded and rendered as a real entry.
60 changes: 60 additions & 0 deletions tools/changelog-validator/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import fs from "fs/promises";
import { readFileSync } from "fs";
import { glob } from "tinyglobby";
import yaml from "js-yaml";
import { fileURLToPath } from "url";

import Ajv from "ajv";
import addFormats from "ajv-formats";

const ajv = new Ajv({ allErrors: true, strict: false });
addFormats(ajv);

async function validateChangelogs() {
const schema = JSON.parse(
readFileSync("../../app/_data/schemas/changelog/konnect.json", "utf-8"),
);
const validate = ajv.compile(schema);

const files = await glob(["app/_data/changelogs/konnect/*.{yml,yaml}"], {
cwd: "../../",
});

const errors = [];
for (const filePath of files) {
const file = await fs.readFile(`../../${filePath}`, "utf-8");
const data = yaml.load(file);

// js-yaml parses unquoted ISO dates (e.g. `date: 2026-07-31`) into a
// native Date, but the schema validates the YAML source as a string.
if (data && data.date instanceof Date) {
data.date = data.date.toISOString().slice(0, 10);
}

if (!validate(data)) {
errors.push({ filePath, errors: validate.errors });
}
}

if (errors.length) {
console.log(`Errors: ${errors.length}`);
for (const error of errors) {
console.log(error.filePath);
console.log(JSON.stringify(error.errors, null, 2));
}
process.exit(1);
}

console.log(`No invalid changelog entries detected (${files.length} checked).`);
}

if (process.argv[1] === fileURLToPath(import.meta.url)) {
try {
validateChangelogs();
} catch (e) {
console.log(e);
process.exit(1);
}
}

export default validateChangelogs;
Loading
Loading