diff --git a/.github/prompts/generate-changelog.md b/.github/prompts/generate-changelog.md index cbcedc5c56e..a5b3f335977 100644 --- a/.github/prompts/generate-changelog.md +++ b/.github/prompts/generate-changelog.md @@ -1,18 +1,22 @@ -# Weekly Changelog Generation +# Changelog Generation for Release -Generate a new changelog entry for this week based on merged PRs. +Generate a new changelog entry for this release based on merged PRs since the previous tag. + +The version number is provided at the top of the prompt as `Version: X.Y.Z`. ## Instructions -1. **Find PRs merged since last Monday** - - Use `gh pr list --state merged --search "merged:>=$(date -d 'last monday' +%Y-%m-%d)" --json number,title,body,url,mergedAt --limit 50` to get all PRs merged in the past week +1. **Find PRs merged since the previous tag** + - Find the previous `desktop-v*` tag: `git tag --sort=-v:refname -l "desktop-v*" | head -2 | tail -1` to get the second-most-recent tag (alternatively try `git describe --tags --abbrev=0 --match "desktop-v*" HEAD^ 2>/dev/null` if HEAD is the tagged commit) + - Get the date of that tag: `git log -1 --format=%aI ` + - Use `gh pr list --state merged --search "merged:>=$(date -d '' +%Y-%m-%d 2>/dev/null || date -j -f '%Y-%m-%dT%H:%M:%S%z' '' +%Y-%m-%d)" --json number,title,body,url,mergedAt --limit 50` to get all PRs merged since the previous tag - Categorize PRs into: **Major features**, **Improvements**, **Bug fixes** - Skip PRs that are purely internal (CI/CD, dev tooling, refactors) unless they affect users 2. **Check for existing changelog** - - Before creating a new file, check if a changelog already exists for this week's date - - Use `ls apps/marketing/content/changelog/` to see existing files - - If a file for today's date already exists, skip creation and report that a changelog already exists + - Before creating a new file, check if a changelog already exists for this version + - Use `grep -rl "version: X.Y.Z" apps/marketing/content/changelog/` to search for existing files with this version in frontmatter + - If a file for this version already exists, skip creation and report that a changelog already exists 3. **Prioritize content** - **Lead with 2-4 major features** - These get their own sections with full descriptions @@ -20,9 +24,10 @@ Generate a new changelog entry for this week based on merged PRs. - **Bug fixes go in a footnote section** - Brief one-liner summaries at the bottom 4. **Create the changelog file** - - Create a new file at: `apps/marketing/content/changelog/YYYY-MM-DD-slug.mdx` - - Use today's date for the filename (e.g., `2026-01-27-descriptive-slug.mdx`) - - The slug should summarize the main features (e.g., `terminal-improvements`, `sidebar-workspaces`) + - Create a new file at: `apps/marketing/content/changelog/YYYY-MM-DD-descriptive-slug.mdx` + - Use today's date and a short descriptive slug based on the main features (e.g., `2026-02-16-in-app-browser.mdx`) + - The slug should be SEO-friendly and summarize the key changes (e.g., `terminal-improvements`, `sidebar-workspaces`) + - Do NOT put the version number in the filename — the version lives in frontmatter only 5. **Follow this exact format**: @@ -30,6 +35,7 @@ Generate a new changelog entry for this week based on merged PRs. --- title: Brief title highlighting 1-2 main features date: YYYY-MM-DD +version: X.Y.Z image: /changelog/IMAGE_PLACEHOLDER.png --- @@ -59,6 +65,7 @@ Brief description of the feature and its benefit to users. 6. **Important formatting rules** - Frontmatter (`---`) must be at the very top of the file with no content before it - MDX comments (`{/* ... */}`) must come AFTER the frontmatter, not before + - Include `version: X.Y.Z` in the frontmatter (use the version number provided, without the `v` prefix) - Set `image:` in frontmatter to `/changelog/IMAGE_PLACEHOLDER.png` - reviewers will replace this - Add TODO comments for features that would benefit from screenshots - Use a horizontal rule (`---`) before the bug fixes footnote @@ -89,4 +96,4 @@ Read these files to understand the expected format: ## Output -Create exactly one new changelog file. If there are no significant PRs to report or a changelog already exists for this week, do not create a file and report why. +Create exactly one new changelog file. If there are no significant PRs to report or a changelog already exists for this version, do not create a file and report why. diff --git a/.github/workflows/generate-changelog.yml b/.github/workflows/generate-changelog.yml index 64466056fb7..f74150a16a4 100644 --- a/.github/workflows/generate-changelog.yml +++ b/.github/workflows/generate-changelog.yml @@ -1,10 +1,13 @@ name: Generate Changelog on: - schedule: - # Run every Monday at 9:00 AM UTC - - cron: "0 9 * * 1" - workflow_dispatch: # Allow manual triggering + release: + types: [published] + workflow_dispatch: + inputs: + version: + description: "Version number (e.g. 0.0.76)" + required: true jobs: generate-changelog: @@ -20,6 +23,26 @@ jobs: with: fetch-depth: 0 + - name: Extract version + id: version + env: + INPUT_VERSION: ${{ github.event.inputs.version }} + RELEASE_TAG: ${{ github.event.release.tag_name }} + EVENT_NAME: ${{ github.event_name }} + run: | + if [ "$EVENT_NAME" = "workflow_dispatch" ]; then + VERSION="$INPUT_VERSION" + else + # Extract version from release tag: desktop-v0.0.76 -> 0.0.76 + VERSION="${RELEASE_TAG#desktop-v}" + fi + if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then + echo "::error::Invalid version format: $VERSION (expected X.Y.Z)" + exit 1 + fi + echo "VERSION=$VERSION" >> $GITHUB_ENV + echo "version=$VERSION" >> $GITHUB_OUTPUT + - name: Setup Bun uses: oven-sh/setup-bun@v2 with: @@ -35,11 +58,11 @@ jobs: run: bun install --frozen - name: Install Claude Code - run: npm install -g @anthropic-ai/claude-code + run: bun install -g @anthropic-ai/claude-code - name: Create branch run: | - BRANCH_NAME="changelog/$(date +%Y-%m-%d)-${GITHUB_RUN_ID}" + BRANCH_NAME="changelog/v${VERSION}-${GITHUB_RUN_ID}" git checkout -B "$BRANCH_NAME" echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV @@ -48,7 +71,11 @@ jobs: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - claude -p "$(cat .github/prompts/generate-changelog.md)" --allowedTools "Bash(git*)" "Bash(gh*)" "Bash(date*)" "Bash(ls*)" "Read" "Write" "Edit" "Glob" "Grep" + { + echo "Version: $VERSION" + echo "" + cat .github/prompts/generate-changelog.md + } | claude -p --allowedTools "Bash(git*)" "Bash(gh*)" "Bash(date*)" "Bash(ls*)" "Read" "Write" "Edit" "Glob" "Grep" - name: Check for changes id: check-changes @@ -69,24 +96,30 @@ jobs: git config --local user.email "github-actions[bot]@users.noreply.github.com" git config --local user.name "github-actions[bot]" git add -A - git commit -m "docs: generate weekly changelog $(date +%Y-%m-%d)" + git commit -m "docs: generate changelog for v${VERSION}" git push origin "$BRANCH_NAME" - name: Create Pull Request if: steps.check-changes.outputs.has_changes == 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + EVENT_NAME: ${{ github.event_name }} run: | - CURRENT_DATE=$(date +%Y-%m-%d) + TAG_LINE="" + if [ "$EVENT_NAME" = "release" ]; then + TAG_LINE="- **Tag**: desktop-v${VERSION}" + fi + gh pr create \ --reviewer kitenite --reviewer saddlepaddle --reviewer AviPeltz \ - --title "docs: weekly changelog - $CURRENT_DATE" \ + --title "docs: changelog for v${VERSION}" \ --body "## Summary - This PR was automatically generated by Claude Code to create the weekly changelog. + This PR was automatically generated by Claude Code to create the changelog for v${VERSION}. ## Details - **Prompt file**: \`.github/prompts/generate-changelog.md\` - - **Generated on**: $CURRENT_DATE + - **Version**: v${VERSION} + ${TAG_LINE} ## Review Checklist - [ ] Replace \`IMAGE_PLACEHOLDER.png\` with actual screenshot diff --git a/apps/marketing/src/app/changelog.xml/route.ts b/apps/marketing/src/app/changelog.xml/route.ts index 410c8c9bf9a..58006b20967 100644 --- a/apps/marketing/src/app/changelog.xml/route.ts +++ b/apps/marketing/src/app/changelog.xml/route.ts @@ -26,7 +26,7 @@ export async function GET() { .map( (entry) => ` - ${escapeXml(entry.title)} + ${entry.version ? `v${escapeXml(entry.version)} — ` : ""}${escapeXml(entry.title)} ${baseUrl}/changelog/${entry.slug} ${escapeXml(entry.description || "")} ${new Date(entry.date).toUTCString()} diff --git a/apps/marketing/src/app/changelog/[slug]/components/ChangelogEntryLayout/ChangelogEntryLayout.tsx b/apps/marketing/src/app/changelog/[slug]/components/ChangelogEntryLayout/ChangelogEntryLayout.tsx index aa9e9d1b0e7..eba60df740b 100644 --- a/apps/marketing/src/app/changelog/[slug]/components/ChangelogEntryLayout/ChangelogEntryLayout.tsx +++ b/apps/marketing/src/app/changelog/[slug]/components/ChangelogEntryLayout/ChangelogEntryLayout.tsx @@ -6,7 +6,7 @@ import type { ReactNode } from "react"; import { GridCross } from "@/app/blog/components/GridCross"; import { type ChangelogEntry, - formatChangelogDate, + formatVersionDate, } from "@/lib/changelog-utils"; interface ChangelogEntryLayoutProps { @@ -18,8 +18,6 @@ export function ChangelogEntryLayout({ entry, children, }: ChangelogEntryLayoutProps) { - const formattedDate = formatChangelogDate(entry.date); - return (
{/* Grid background with dashed lines */} @@ -44,7 +42,7 @@ export function ChangelogEntryLayout({ dateTime={entry.date} className="text-sm font-mono text-muted-foreground uppercase tracking-wider" > - {formattedDate} + {formatVersionDate(entry.version, entry.date)}

diff --git a/apps/marketing/src/app/changelog/[slug]/opengraph-image.tsx b/apps/marketing/src/app/changelog/[slug]/opengraph-image.tsx index 0c9b3d76823..cec53849bca 100644 --- a/apps/marketing/src/app/changelog/[slug]/opengraph-image.tsx +++ b/apps/marketing/src/app/changelog/[slug]/opengraph-image.tsx @@ -2,7 +2,7 @@ import fs from "node:fs"; import path from "node:path"; import { ImageResponse } from "next/og"; import { getChangelogEntry } from "@/lib/changelog"; -import { formatChangelogDate } from "@/lib/changelog-utils"; +import { formatVersionDate } from "@/lib/changelog-utils"; export const alt = "Superset Changelog"; export const size = { width: 1200, height: 630 }; @@ -142,7 +142,7 @@ export default async function Image({ {entry.title}
- {formatChangelogDate(entry.date)} + {formatVersionDate(entry.version, entry.date)}
diff --git a/apps/marketing/src/app/changelog/components/ChangelogCard/ChangelogCard.tsx b/apps/marketing/src/app/changelog/components/ChangelogCard/ChangelogCard.tsx index 311e543fa59..b77a983bd22 100644 --- a/apps/marketing/src/app/changelog/components/ChangelogCard/ChangelogCard.tsx +++ b/apps/marketing/src/app/changelog/components/ChangelogCard/ChangelogCard.tsx @@ -2,7 +2,7 @@ import Image from "next/image"; import Link from "next/link"; import { type ChangelogEntry, - formatChangelogDate, + formatVersionDate, } from "@/lib/changelog-utils"; interface ChangelogCardProps { @@ -10,8 +10,6 @@ interface ChangelogCardProps { } export function ChangelogCard({ entry }: ChangelogCardProps) { - const formattedDate = formatChangelogDate(entry.date); - return (
@@ -30,7 +28,7 @@ export function ChangelogCard({ entry }: ChangelogCardProps) { dateTime={entry.date} className="text-sm font-mono text-muted-foreground" > - {formattedDate} + {formatVersionDate(entry.version, entry.date)}

{entry.title} diff --git a/apps/marketing/src/app/changelog/components/ChangelogEntry/ChangelogEntry.tsx b/apps/marketing/src/app/changelog/components/ChangelogEntry/ChangelogEntry.tsx index 849e244b30e..06de3ff3c29 100644 --- a/apps/marketing/src/app/changelog/components/ChangelogEntry/ChangelogEntry.tsx +++ b/apps/marketing/src/app/changelog/components/ChangelogEntry/ChangelogEntry.tsx @@ -2,7 +2,7 @@ import Link from "next/link"; import { MDXRemote } from "next-mdx-remote/rsc"; import { type ChangelogEntry as ChangelogEntryType, - formatChangelogDate, + formatVersionDate, } from "@/lib/changelog-utils"; import { changelogMdxComponents } from "./changelog-mdx-components"; @@ -11,8 +11,6 @@ interface ChangelogEntryProps { } export async function ChangelogEntry({ entry }: ChangelogEntryProps) { - const formattedDate = formatChangelogDate(entry.date); - return (
- {formattedDate} + {formatVersionDate(entry.version, entry.date)}
@@ -36,7 +34,7 @@ export async function ChangelogEntry({ entry }: ChangelogEntryProps) { dateTime={entry.date} className="lg:hidden block text-sm font-mono text-muted-foreground mb-4" > - {formattedDate} + {formatVersionDate(entry.version, entry.date)} {/* Title */} diff --git a/apps/marketing/src/lib/changelog-utils.ts b/apps/marketing/src/lib/changelog-utils.ts index d7337fd5fb8..ddab1bbc8a1 100644 --- a/apps/marketing/src/lib/changelog-utils.ts +++ b/apps/marketing/src/lib/changelog-utils.ts @@ -11,6 +11,7 @@ export interface ChangelogEntry { title: string; description?: string; date: string; + version?: string; image?: string; content: string; } @@ -20,3 +21,12 @@ export { slugify } from "./content-utils"; export function formatChangelogDate(date: string): string { return formatContentDate(date, "long"); } + +export function formatVersionDate( + version: string | undefined, + date: string, + separator = "·", +): string { + const formatted = formatChangelogDate(date); + return version ? `v${version} ${separator} ${formatted}` : formatted; +} diff --git a/apps/marketing/src/lib/changelog.ts b/apps/marketing/src/lib/changelog.ts index 936c949cfd7..611fece0c2f 100644 --- a/apps/marketing/src/lib/changelog.ts +++ b/apps/marketing/src/lib/changelog.ts @@ -6,7 +6,7 @@ import { normalizeContentDate } from "./content-utils"; export { type ChangelogEntry, - formatChangelogDate, + formatVersionDate, slugify, } from "./changelog-utils"; @@ -26,6 +26,7 @@ function parseFrontmatter(filePath: string): ChangelogEntry | null { title: data.title ?? "Untitled", description: data.description, date: dateValue, + version: data.version, image: data.image, content, };