From 9b858ee8fb9de45e926d692da268d4c7ee9724ba Mon Sep 17 00:00:00 2001 From: Shreyag02 Date: Thu, 29 Oct 2026 17:19:35 +0530 Subject: [PATCH 1/8] feat: [docs] add a changelog page --- apps/www/package.json | 2 +- apps/www/src/components/docs/changelog.tsx | 38 ++++++++++ .../www/src/components/mdx/mdx-components.tsx | 2 + .../src/content/docs/(overview)/changelog.mdx | 8 +++ .../www/src/content/docs/(overview)/meta.json | 2 +- apps/www/src/lib/changelog.ts | 71 +++++++++++++++++++ 6 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 apps/www/src/components/docs/changelog.tsx create mode 100644 apps/www/src/content/docs/(overview)/changelog.mdx create mode 100644 apps/www/src/lib/changelog.ts diff --git a/apps/www/package.json b/apps/www/package.json index b5ae77297..199a6aff0 100644 --- a/apps/www/package.json +++ b/apps/www/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "scripts": { - "prebuild": "cd ../.. && npm run build:apsara", + "prebuild": "cd ../.. && (git rev-parse -q --verify refs/tags/v1.0.0 >/dev/null || git fetch -q --tags $(test \"$(git rev-parse --is-shallow-repository)\" = true && echo --unshallow) https://github.com/raystack/apsara.git || true) && npm run build:apsara", "dev": "next dev", "build": "next build", "start": "next start", diff --git a/apps/www/src/components/docs/changelog.tsx b/apps/www/src/components/docs/changelog.tsx new file mode 100644 index 000000000..6cd97277c --- /dev/null +++ b/apps/www/src/components/docs/changelog.tsx @@ -0,0 +1,38 @@ +import Link from 'fumadocs-core/link'; +import { getChangelog, RELEASES_URL } from '@/lib/changelog'; +import styles from '../mdx/mdx-components.module.css'; + +const PULL_URL = 'https://github.com/raystack/apsara/pull'; + +/** The pull requests of each release, from the git history. */ +export function Changelog() { + const releases = getChangelog(); + if (!releases) + return ( +

+ See GitHub Releases. +

+ ); + + return releases.map(release => ( +
+

+ {release.tag ? ( + {release.tag} + ) : ( + 'Unreleased' + )} +

+ +
+ )); +} diff --git a/apps/www/src/components/mdx/mdx-components.tsx b/apps/www/src/components/mdx/mdx-components.tsx index adab360e5..443ad4a49 100644 --- a/apps/www/src/components/mdx/mdx-components.tsx +++ b/apps/www/src/components/mdx/mdx-components.tsx @@ -11,6 +11,7 @@ import type { TableHTMLAttributes } from 'react'; import Demo from '../demo'; +import { Changelog } from '../docs/changelog'; import { IconGallery } from '../icongallery'; import { TokenTable } from '../tokentable'; import { TypeTable } from '../typetable'; @@ -104,6 +105,7 @@ const mdxComponents = { ), IconGallery, + Changelog, Callout: (props: ComponentPropsWithoutRef) => ( diff --git a/apps/www/src/content/docs/(overview)/meta.json b/apps/www/src/content/docs/(overview)/meta.json index d149ba563..21dbd2e4e 100644 --- a/apps/www/src/content/docs/(overview)/meta.json +++ b/apps/www/src/content/docs/(overview)/meta.json @@ -1,4 +1,4 @@ { "title": "Overview", - "pages": ["..."] + "pages": ["...", "changelog"] } diff --git a/apps/www/src/lib/changelog.ts b/apps/www/src/lib/changelog.ts new file mode 100644 index 000000000..f36eba7c9 --- /dev/null +++ b/apps/www/src/lib/changelog.ts @@ -0,0 +1,71 @@ +import { execFileSync } from 'node:child_process'; + +export const RELEASES_URL = 'https://github.com/raystack/apsara/releases'; + +// Releases before v1.0.0 are the Radix-based library. +const FIRST_TAG = 'v1.0.0'; +const TYPES = new Set(['feat', 'fix', 'refactor']); +const SUBJECT = + /^(\w+)(?:\(([^)]*)\))?!?:\s*(?:\[([^\]]*)\]\s*)?(.+?)\s*\(#(\d+)\)$/; + +export interface ChangelogEntry { + type: string; + scopes: string[]; + summary: string; + pr: number; +} + +export interface ChangelogRelease { + /** `null` for the changes after the latest release. */ + tag: string | null; + entries: ChangelogEntry[]; +} + +/** + * Reads a squash-merged subject, `: [] (#)` or + * `(): (#)`. + */ +function parseSubject(subject: string): ChangelogEntry | undefined { + const match = SUBJECT.exec(subject); + if (!match || !TYPES.has(match[1])) return undefined; + const [, type, parenScopes, bracketScopes, summary, pr] = match; + const scopes = (bracketScopes ?? parenScopes ?? '') + .split(',') + .map(scope => scope.trim()) + .filter(Boolean); + return { type, scopes, summary, pr: Number(pr) }; +} + +function git(args: string[]) { + return execFileSync('git', args, { + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'ignore'] + }); +} + +/** + * Returns the feat, fix and refactor pull requests of each release after + * v1.0.0, newest first, or `null` when the build has no git history or tags. + */ +export function getChangelog(): ChangelogRelease[] | null { + try { + const tags = git(['tag', '--merged', 'HEAD', '--sort=-v:refname']) + .split('\n') + .filter(tag => /^v\d+\.\d+\.\d+$/.test(tag)); + const first = tags.indexOf(FIRST_TAG); + if (first === -1) return null; + const ranges: [string | null, string][] = [[null, `${tags[0]}..HEAD`]]; + for (let index = 0; index < first; index += 1) + ranges.push([tags[index], `${tags[index + 1]}..${tags[index]}`]); + return ranges + .map(([tag, range]) => ({ + tag, + entries: git(['log', '--no-merges', '--format=%s', range]) + .split('\n') + .flatMap(subject => parseSubject(subject) ?? []) + })) + .filter(release => release.entries.length > 0); + } catch { + return null; + } +} From b1ecc841237ccf02779a10e6cb14218a90ea4ee5 Mon Sep 17 00:00:00 2001 From: Shreyag02 Date: Fri, 30 Oct 2026 09:12:20 +0530 Subject: [PATCH 2/8] refactor: [docs] keep the changelog code in one file --- apps/www/src/components/docs/changelog.tsx | 71 +++++++++++++++++++++- apps/www/src/lib/changelog.ts | 71 ---------------------- 2 files changed, 70 insertions(+), 72 deletions(-) delete mode 100644 apps/www/src/lib/changelog.ts diff --git a/apps/www/src/components/docs/changelog.tsx b/apps/www/src/components/docs/changelog.tsx index 6cd97277c..c737cf52d 100644 --- a/apps/www/src/components/docs/changelog.tsx +++ b/apps/www/src/components/docs/changelog.tsx @@ -1,9 +1,78 @@ +import { execFileSync } from 'node:child_process'; import Link from 'fumadocs-core/link'; -import { getChangelog, RELEASES_URL } from '@/lib/changelog'; import styles from '../mdx/mdx-components.module.css'; +const RELEASES_URL = 'https://github.com/raystack/apsara/releases'; const PULL_URL = 'https://github.com/raystack/apsara/pull'; +// Releases before v1.0.0 are the Radix-based library. +const FIRST_TAG = 'v1.0.0'; +const TYPES = new Set(['feat', 'fix', 'refactor']); +const SUBJECT = + /^(\w+)(?:\(([^)]*)\))?!?:\s*(?:\[([^\]]*)\]\s*)?(.+?)\s*\(#(\d+)\)$/; + +interface ChangelogEntry { + type: string; + scopes: string[]; + summary: string; + pr: number; +} + +interface ChangelogRelease { + /** `null` for the changes after the latest release. */ + tag: string | null; + entries: ChangelogEntry[]; +} + +/** + * Reads a squash-merged subject, `: [] (#)` or + * `(): (#)`. + */ +function parseSubject(subject: string): ChangelogEntry | undefined { + const match = SUBJECT.exec(subject); + if (!match || !TYPES.has(match[1])) return undefined; + const [, type, parenScopes, bracketScopes, summary, pr] = match; + const scopes = (bracketScopes ?? parenScopes ?? '') + .split(',') + .map(scope => scope.trim()) + .filter(Boolean); + return { type, scopes, summary, pr: Number(pr) }; +} + +function git(args: string[]) { + return execFileSync('git', args, { + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'ignore'] + }); +} + +/** + * Returns the feat, fix and refactor pull requests of each release after + * v1.0.0, newest first, or `null` when the build has no git history or tags. + */ +function getChangelog(): ChangelogRelease[] | null { + try { + const tags = git(['tag', '--merged', 'HEAD', '--sort=-v:refname']) + .split('\n') + .filter(tag => /^v\d+\.\d+\.\d+$/.test(tag)); + const first = tags.indexOf(FIRST_TAG); + if (first === -1) return null; + const ranges: [string | null, string][] = [[null, `${tags[0]}..HEAD`]]; + for (let index = 0; index < first; index += 1) + ranges.push([tags[index], `${tags[index + 1]}..${tags[index]}`]); + return ranges + .map(([tag, range]) => ({ + tag, + entries: git(['log', '--no-merges', '--format=%s', range]) + .split('\n') + .flatMap(subject => parseSubject(subject) ?? []) + })) + .filter(release => release.entries.length > 0); + } catch { + return null; + } +} + /** The pull requests of each release, from the git history. */ export function Changelog() { const releases = getChangelog(); diff --git a/apps/www/src/lib/changelog.ts b/apps/www/src/lib/changelog.ts deleted file mode 100644 index f36eba7c9..000000000 --- a/apps/www/src/lib/changelog.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { execFileSync } from 'node:child_process'; - -export const RELEASES_URL = 'https://github.com/raystack/apsara/releases'; - -// Releases before v1.0.0 are the Radix-based library. -const FIRST_TAG = 'v1.0.0'; -const TYPES = new Set(['feat', 'fix', 'refactor']); -const SUBJECT = - /^(\w+)(?:\(([^)]*)\))?!?:\s*(?:\[([^\]]*)\]\s*)?(.+?)\s*\(#(\d+)\)$/; - -export interface ChangelogEntry { - type: string; - scopes: string[]; - summary: string; - pr: number; -} - -export interface ChangelogRelease { - /** `null` for the changes after the latest release. */ - tag: string | null; - entries: ChangelogEntry[]; -} - -/** - * Reads a squash-merged subject, `: [] (#)` or - * `(): (#)`. - */ -function parseSubject(subject: string): ChangelogEntry | undefined { - const match = SUBJECT.exec(subject); - if (!match || !TYPES.has(match[1])) return undefined; - const [, type, parenScopes, bracketScopes, summary, pr] = match; - const scopes = (bracketScopes ?? parenScopes ?? '') - .split(',') - .map(scope => scope.trim()) - .filter(Boolean); - return { type, scopes, summary, pr: Number(pr) }; -} - -function git(args: string[]) { - return execFileSync('git', args, { - encoding: 'utf8', - stdio: ['ignore', 'pipe', 'ignore'] - }); -} - -/** - * Returns the feat, fix and refactor pull requests of each release after - * v1.0.0, newest first, or `null` when the build has no git history or tags. - */ -export function getChangelog(): ChangelogRelease[] | null { - try { - const tags = git(['tag', '--merged', 'HEAD', '--sort=-v:refname']) - .split('\n') - .filter(tag => /^v\d+\.\d+\.\d+$/.test(tag)); - const first = tags.indexOf(FIRST_TAG); - if (first === -1) return null; - const ranges: [string | null, string][] = [[null, `${tags[0]}..HEAD`]]; - for (let index = 0; index < first; index += 1) - ranges.push([tags[index], `${tags[index + 1]}..${tags[index]}`]); - return ranges - .map(([tag, range]) => ({ - tag, - entries: git(['log', '--no-merges', '--format=%s', range]) - .split('\n') - .flatMap(subject => parseSubject(subject) ?? []) - })) - .filter(release => release.entries.length > 0); - } catch { - return null; - } -} From d2c24ff51fd5a83fac7d16659c88c3f0a831c43d Mon Sep 17 00:00:00 2001 From: Shreyag02 Date: Mon, 2 Nov 2026 01:57:10 +0530 Subject: [PATCH 3/8] fix: [docs] mark breaking changes and add the changelog toc --- apps/www/source.config.ts | 2 + apps/www/src/components/docs/changelog.tsx | 107 ----------- .../www/src/components/mdx/mdx-components.tsx | 2 - .../src/content/docs/(overview)/changelog.mdx | 2 +- apps/www/src/lib/ai.ts | 2 + apps/www/src/lib/remark-changelog.ts | 172 ++++++++++++++++++ 6 files changed, 177 insertions(+), 110 deletions(-) delete mode 100644 apps/www/src/components/docs/changelog.tsx create mode 100644 apps/www/src/lib/remark-changelog.ts diff --git a/apps/www/source.config.ts b/apps/www/source.config.ts index a64771a59..c9f674a79 100644 --- a/apps/www/source.config.ts +++ b/apps/www/source.config.ts @@ -13,6 +13,7 @@ import { metaSchema } from 'fumadocs-mdx/config'; import { remarkAutoTypeTable } from 'fumadocs-typescript'; +import { remarkChangelog } from '@/lib/remark-changelog'; import { generator, transform } from '@/lib/type-table'; import { SourceSchema, TagSchema } from '@/lib/types'; @@ -37,6 +38,7 @@ export default defineConfig({ remarkPlugins: () => { return [ remarkGfm, + remarkChangelog, [remarkHeading, { generateToc: false }], [remarkImage, { useImport: undefined }], // remarkCodeTab, diff --git a/apps/www/src/components/docs/changelog.tsx b/apps/www/src/components/docs/changelog.tsx deleted file mode 100644 index c737cf52d..000000000 --- a/apps/www/src/components/docs/changelog.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import { execFileSync } from 'node:child_process'; -import Link from 'fumadocs-core/link'; -import styles from '../mdx/mdx-components.module.css'; - -const RELEASES_URL = 'https://github.com/raystack/apsara/releases'; -const PULL_URL = 'https://github.com/raystack/apsara/pull'; - -// Releases before v1.0.0 are the Radix-based library. -const FIRST_TAG = 'v1.0.0'; -const TYPES = new Set(['feat', 'fix', 'refactor']); -const SUBJECT = - /^(\w+)(?:\(([^)]*)\))?!?:\s*(?:\[([^\]]*)\]\s*)?(.+?)\s*\(#(\d+)\)$/; - -interface ChangelogEntry { - type: string; - scopes: string[]; - summary: string; - pr: number; -} - -interface ChangelogRelease { - /** `null` for the changes after the latest release. */ - tag: string | null; - entries: ChangelogEntry[]; -} - -/** - * Reads a squash-merged subject, `: [] (#)` or - * `(): (#)`. - */ -function parseSubject(subject: string): ChangelogEntry | undefined { - const match = SUBJECT.exec(subject); - if (!match || !TYPES.has(match[1])) return undefined; - const [, type, parenScopes, bracketScopes, summary, pr] = match; - const scopes = (bracketScopes ?? parenScopes ?? '') - .split(',') - .map(scope => scope.trim()) - .filter(Boolean); - return { type, scopes, summary, pr: Number(pr) }; -} - -function git(args: string[]) { - return execFileSync('git', args, { - encoding: 'utf8', - stdio: ['ignore', 'pipe', 'ignore'] - }); -} - -/** - * Returns the feat, fix and refactor pull requests of each release after - * v1.0.0, newest first, or `null` when the build has no git history or tags. - */ -function getChangelog(): ChangelogRelease[] | null { - try { - const tags = git(['tag', '--merged', 'HEAD', '--sort=-v:refname']) - .split('\n') - .filter(tag => /^v\d+\.\d+\.\d+$/.test(tag)); - const first = tags.indexOf(FIRST_TAG); - if (first === -1) return null; - const ranges: [string | null, string][] = [[null, `${tags[0]}..HEAD`]]; - for (let index = 0; index < first; index += 1) - ranges.push([tags[index], `${tags[index + 1]}..${tags[index]}`]); - return ranges - .map(([tag, range]) => ({ - tag, - entries: git(['log', '--no-merges', '--format=%s', range]) - .split('\n') - .flatMap(subject => parseSubject(subject) ?? []) - })) - .filter(release => release.entries.length > 0); - } catch { - return null; - } -} - -/** The pull requests of each release, from the git history. */ -export function Changelog() { - const releases = getChangelog(); - if (!releases) - return ( -

- See GitHub Releases. -

- ); - - return releases.map(release => ( -
-

- {release.tag ? ( - {release.tag} - ) : ( - 'Unreleased' - )} -

-
    - {release.entries.map(entry => ( -
  • - {entry.type}:{' '} - {entry.scopes.length > 0 && `[${entry.scopes.join(', ')}] `} - {entry.summary} ( - #{entry.pr}) -
  • - ))} -
-
- )); -} diff --git a/apps/www/src/components/mdx/mdx-components.tsx b/apps/www/src/components/mdx/mdx-components.tsx index 443ad4a49..adab360e5 100644 --- a/apps/www/src/components/mdx/mdx-components.tsx +++ b/apps/www/src/components/mdx/mdx-components.tsx @@ -11,7 +11,6 @@ import type { TableHTMLAttributes } from 'react'; import Demo from '../demo'; -import { Changelog } from '../docs/changelog'; import { IconGallery } from '../icongallery'; import { TokenTable } from '../tokentable'; import { TypeTable } from '../typetable'; @@ -105,7 +104,6 @@ const mdxComponents = { ), IconGallery, - Changelog, Callout: (props: ComponentPropsWithoutRef) => ( diff --git a/apps/www/src/lib/ai.ts b/apps/www/src/lib/ai.ts index 17b888388..f8d6624e6 100644 --- a/apps/www/src/lib/ai.ts +++ b/apps/www/src/lib/ai.ts @@ -7,11 +7,13 @@ import remarkGfm from 'remark-gfm'; import remarkMdx from 'remark-mdx'; import { docs } from '@/lib/source'; import { remarkTypeTableToMd } from './remark'; +import { remarkChangelog } from './remark-changelog'; import { generator, transform } from './type-table'; const processor = remark() .use(remarkMdx) .use(remarkGfm) + .use(remarkChangelog) .use(remarkAutoTypeTable, { generator, options: { transform } }) .use(remarkTypeTableToMd); diff --git a/apps/www/src/lib/remark-changelog.ts b/apps/www/src/lib/remark-changelog.ts new file mode 100644 index 000000000..79d2d6631 --- /dev/null +++ b/apps/www/src/lib/remark-changelog.ts @@ -0,0 +1,172 @@ +import { execFileSync } from 'node:child_process'; +import type { Link, List, Paragraph, Root, RootContent } from 'mdast'; + +const RELEASES_URL = 'https://github.com/raystack/apsara/releases'; +const PULL_URL = 'https://github.com/raystack/apsara/pull'; + +// Releases before v1.0.0 are the Radix-based library. +const FIRST_TAG = 'v1.0.0'; +const TYPES = new Set(['feat', 'fix', 'refactor']); +// Older subjects put the `!` before or after the parenthesized scope. +const SUBJECT = + /^(\w+)(!?)(?:\(([^)]*)\))?(!?):\s*(?:\[([^\]]*)\]\s*)?(.+?)\s*\(#(\d+)\)$/; +const BREAKING_FOOTER = /^BREAKING[ -]CHANGE:/m; +// Separates commits and the subject from the body in the `git log` output. +const COMMIT_END = '\x1e'; +const BODY_START = '\x1f'; + +interface ChangelogEntry { + type: string; + breaking: boolean; + scopes: string[]; + summary: string; + pr: number; +} + +interface ChangelogRelease { + /** `null` for the changes after the latest release. */ + tag: string | null; + entries: ChangelogEntry[]; +} + +/** + * Reads a squash-merged commit, `: [] (#)` or + * `(): (#)`. A `!` after the type or a + * `BREAKING CHANGE:` footer marks it as breaking. + */ +function parseCommit(commit: string): ChangelogEntry | undefined { + const [subject, body = ''] = commit.trim().split(BODY_START); + const match = SUBJECT.exec(subject); + if (!match || !TYPES.has(match[1])) return undefined; + const [, type, bangBefore, parenScopes, bangAfter, bracketScopes, text, pr] = + match; + // Some older subjects write `BREAKING CHANGE` as the scope or in the summary. + const scopes = (bracketScopes ?? parenScopes ?? '') + .split(',') + .map(scope => scope.trim()) + .filter(scope => scope && scope !== 'BREAKING CHANGE'); + const summary = text.replace(/^BREAKING CHANGE:?\s*/, ''); + const breaking = + Boolean(bangBefore || bangAfter) || + subject.includes('BREAKING CHANGE') || + BREAKING_FOOTER.test(body); + return { type, breaking, scopes, summary, pr: Number(pr) }; +} + +function git(args: string[]) { + return execFileSync('git', args, { + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'ignore'], + maxBuffer: 64 * 1024 * 1024 + }); +} + +/** + * Returns the feat, fix and refactor pull requests of each release after + * v1.0.0, newest first, or `null` when the build has no git history or tags. + */ +function getChangelog(): ChangelogRelease[] | null { + try { + const tags = git(['tag', '--merged', 'HEAD', '--sort=-v:refname']) + .split('\n') + .filter(tag => /^v\d+\.\d+\.\d+$/.test(tag)); + const first = tags.indexOf(FIRST_TAG); + if (first === -1) return null; + const ranges: [string | null, string][] = [[null, `${tags[0]}..HEAD`]]; + for (let index = 0; index < first; index += 1) + ranges.push([tags[index], `${tags[index + 1]}..${tags[index]}`]); + return ranges + .map(([tag, range]) => ({ + tag, + entries: git([ + 'log', + '--no-merges', + `--format=%s${BODY_START}%b${COMMIT_END}`, + range + ]) + .split(COMMIT_END) + .flatMap(commit => parseCommit(commit) ?? []) + })) + .filter(release => release.entries.length > 0); + } catch { + return null; + } +} + +function link(url: string, value: string): Link { + return { type: 'link', url, children: [{ type: 'text', value }] }; +} + +function entryItem(entry: ChangelogEntry): Paragraph { + const scopes = entry.scopes.length > 0 ? `[${entry.scopes.join(', ')}] ` : ''; + return { + type: 'paragraph', + children: [ + { + type: 'text', + value: `${entry.type}${entry.breaking ? '!' : ''}: ${scopes}${entry.summary} (` + }, + link(`${PULL_URL}/${entry.pr}`, `#${entry.pr}`), + { type: 'text', value: ')' } + ] + }; +} + +function changelogNodes(): RootContent[] { + const releases = getChangelog(); + if (!releases) + return [ + { + type: 'paragraph', + children: [ + { type: 'text', value: 'See ' }, + link(RELEASES_URL, 'GitHub Releases'), + { type: 'text', value: '.' } + ] + } + ]; + + return releases.flatMap((release): RootContent[] => { + const list: List = { + type: 'list', + ordered: false, + spread: false, + children: release.entries.map(entry => ({ + type: 'listItem', + spread: false, + children: [entryItem(entry)] + })) + }; + // The table of contents links each heading, so the heading holds no link. + const heading: RootContent = { + type: 'heading', + depth: 2, + children: [{ type: 'text', value: release.tag ?? 'Unreleased' }] + }; + if (!release.tag) return [heading, list]; + return [ + heading, + { + type: 'paragraph', + children: [ + link(`${RELEASES_URL}/tag/${release.tag}`, 'Release on GitHub') + ] + }, + list + ]; + }); +} + +/** + * Replaces `` with a heading and a list of pull requests for each + * release. The plugin writes Markdown, so the headings reach the table of + * contents, search and the page's Markdown output. + */ +export function remarkChangelog() { + return (tree: Root) => { + const index = tree.children.findIndex( + node => node.type === 'mdxJsxFlowElement' && node.name === 'Changelog' + ); + if (index !== -1) tree.children.splice(index, 1, ...changelogNodes()); + }; +} From 7404d02620a2911743ce663ca7fc96897bb31b36 Mon Sep 17 00:00:00 2001 From: Shreyag02 Date: Thu, 12 Nov 2026 12:34:42 +0530 Subject: [PATCH 4/8] fix: [docs] remove the unreleased section from the changelog --- apps/www/src/lib/remark-changelog.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/apps/www/src/lib/remark-changelog.ts b/apps/www/src/lib/remark-changelog.ts index 79d2d6631..a0b542e74 100644 --- a/apps/www/src/lib/remark-changelog.ts +++ b/apps/www/src/lib/remark-changelog.ts @@ -24,8 +24,7 @@ interface ChangelogEntry { } interface ChangelogRelease { - /** `null` for the changes after the latest release. */ - tag: string | null; + tag: string; entries: ChangelogEntry[]; } @@ -72,7 +71,7 @@ function getChangelog(): ChangelogRelease[] | null { .filter(tag => /^v\d+\.\d+\.\d+$/.test(tag)); const first = tags.indexOf(FIRST_TAG); if (first === -1) return null; - const ranges: [string | null, string][] = [[null, `${tags[0]}..HEAD`]]; + const ranges: [string, string][] = []; for (let index = 0; index < first; index += 1) ranges.push([tags[index], `${tags[index + 1]}..${tags[index]}`]); return ranges @@ -141,9 +140,8 @@ function changelogNodes(): RootContent[] { const heading: RootContent = { type: 'heading', depth: 2, - children: [{ type: 'text', value: release.tag ?? 'Unreleased' }] + children: [{ type: 'text', value: release.tag }] }; - if (!release.tag) return [heading, list]; return [ heading, { From 2e5a5eee6850cae621a63d4f2297e86e27daba73 Mon Sep 17 00:00:00 2001 From: Shreyag02 Date: Thu, 12 Nov 2026 13:35:03 +0530 Subject: [PATCH 5/8] fix: [docs] simplify the changelog description --- apps/www/src/content/docs/(overview)/changelog.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/www/src/content/docs/(overview)/changelog.mdx b/apps/www/src/content/docs/(overview)/changelog.mdx index 6d6cc5fb2..491077200 100644 --- a/apps/www/src/content/docs/(overview)/changelog.mdx +++ b/apps/www/src/content/docs/(overview)/changelog.mdx @@ -1,8 +1,8 @@ --- title: Changelog -description: The pull requests in each Apsara release, newest first. +description: Changelogs for each Apsara release. --- -Each entry is a merged pull request. A `!` after the type marks a breaking change. For the steps a breaking change needs, see [Upgrading](/docs/upgrading). +A `!` after the type marks a breaking change. See [Upgrading](/docs/upgrading). From 1a35b58e953484f63701bcc1dadbaec356e6c518 Mon Sep 17 00:00:00 2001 From: Shreyag02 Date: Thu, 12 Nov 2026 13:40:51 +0530 Subject: [PATCH 6/8] Revert "fix: [docs] simplify the changelog description" This reverts commit 2e5a5eee6850cae621a63d4f2297e86e27daba73. --- apps/www/src/content/docs/(overview)/changelog.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/www/src/content/docs/(overview)/changelog.mdx b/apps/www/src/content/docs/(overview)/changelog.mdx index 491077200..6d6cc5fb2 100644 --- a/apps/www/src/content/docs/(overview)/changelog.mdx +++ b/apps/www/src/content/docs/(overview)/changelog.mdx @@ -1,8 +1,8 @@ --- title: Changelog -description: Changelogs for each Apsara release. +description: The pull requests in each Apsara release, newest first. --- -A `!` after the type marks a breaking change. See [Upgrading](/docs/upgrading). +Each entry is a merged pull request. A `!` after the type marks a breaking change. For the steps a breaking change needs, see [Upgrading](/docs/upgrading). From 82561763104e31308a1694d45a8e41c331616284 Mon Sep 17 00:00:00 2001 From: Shreyag02 Date: Thu, 12 Nov 2026 13:58:33 +0530 Subject: [PATCH 7/8] fix: [docs] simplify the changelog page description --- apps/www/src/content/docs/(overview)/changelog.mdx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/www/src/content/docs/(overview)/changelog.mdx b/apps/www/src/content/docs/(overview)/changelog.mdx index 6d6cc5fb2..c8c12616d 100644 --- a/apps/www/src/content/docs/(overview)/changelog.mdx +++ b/apps/www/src/content/docs/(overview)/changelog.mdx @@ -1,8 +1,6 @@ --- title: Changelog -description: The pull requests in each Apsara release, newest first. +description: Changelogs for each Apsara release. --- -Each entry is a merged pull request. A `!` after the type marks a breaking change. For the steps a breaking change needs, see [Upgrading](/docs/upgrading). - From b96f491b60884d0d495ea2bec4a433fa1f27f2bc Mon Sep 17 00:00:00 2001 From: Shreyag02 Date: Thu, 12 Nov 2026 14:25:58 +0530 Subject: [PATCH 8/8] refactor: [docs] drop unused commit subject cases from the changelog --- apps/www/src/lib/remark-changelog.ts | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/apps/www/src/lib/remark-changelog.ts b/apps/www/src/lib/remark-changelog.ts index a0b542e74..6e2a8f7a8 100644 --- a/apps/www/src/lib/remark-changelog.ts +++ b/apps/www/src/lib/remark-changelog.ts @@ -7,9 +7,8 @@ const PULL_URL = 'https://github.com/raystack/apsara/pull'; // Releases before v1.0.0 are the Radix-based library. const FIRST_TAG = 'v1.0.0'; const TYPES = new Set(['feat', 'fix', 'refactor']); -// Older subjects put the `!` before or after the parenthesized scope. const SUBJECT = - /^(\w+)(!?)(?:\(([^)]*)\))?(!?):\s*(?:\[([^\]]*)\]\s*)?(.+?)\s*\(#(\d+)\)$/; + /^(\w+)(!?)(?:\(([^)]*)\))?:\s*(?:\[([^\]]*)\]\s*)?(.+?)\s*\(#(\d+)\)$/; const BREAKING_FOOTER = /^BREAKING[ -]CHANGE:/m; // Separates commits and the subject from the body in the `git log` output. const COMMIT_END = '\x1e'; @@ -37,18 +36,12 @@ function parseCommit(commit: string): ChangelogEntry | undefined { const [subject, body = ''] = commit.trim().split(BODY_START); const match = SUBJECT.exec(subject); if (!match || !TYPES.has(match[1])) return undefined; - const [, type, bangBefore, parenScopes, bangAfter, bracketScopes, text, pr] = - match; - // Some older subjects write `BREAKING CHANGE` as the scope or in the summary. + const [, type, bang, parenScopes, bracketScopes, summary, pr] = match; const scopes = (bracketScopes ?? parenScopes ?? '') .split(',') .map(scope => scope.trim()) - .filter(scope => scope && scope !== 'BREAKING CHANGE'); - const summary = text.replace(/^BREAKING CHANGE:?\s*/, ''); - const breaking = - Boolean(bangBefore || bangAfter) || - subject.includes('BREAKING CHANGE') || - BREAKING_FOOTER.test(body); + .filter(Boolean); + const breaking = Boolean(bang) || BREAKING_FOOTER.test(body); return { type, breaking, scopes, summary, pr: Number(pr) }; }