diff --git a/.github/workflows/update-docs-changelog.yml b/.github/workflows/update-docs-changelog.yml new file mode 100644 index 00000000000000..5e904fe759d569 --- /dev/null +++ b/.github/workflows/update-docs-changelog.yml @@ -0,0 +1,70 @@ +name: Update Docs Changelog + +on: + # Run daily at midnight UTC + schedule: + - cron: '0 0 * * *' + # Allow manual trigger + workflow_dispatch: + +jobs: + update-changelog: + runs-on: ubuntu-latest + # Only run on the main repo, not forks + if: github.repository == 'getsentry/sentry-docs' + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Get auth token + id: token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ vars.SENTRY_INTERNAL_APP_ID }} + private-key: ${{ secrets.SENTRY_INTERNAL_APP_PRIVATE_KEY }} + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Update docs changelog + env: + GITHUB_TOKEN: ${{ steps.token.outputs.token }} + run: node scripts/update-docs-changelog.mjs + + - name: Check for changes + id: changes + run: | + if git diff --quiet includes/docs-changelog.mdx; then + echo "changed=false" >> $GITHUB_OUTPUT + else + echo "changed=true" >> $GITHUB_OUTPUT + fi + + - name: Create PR with changes + if: steps.changes.outputs.changed == 'true' + env: + GITHUB_TOKEN: ${{ steps.token.outputs.token }} + run: | + git config user.email "bot@getsentry.com" + git config user.name "getsentry-bot" + + branch="bot/update-docs-changelog-$(date +%Y%m%d)" + git checkout -b "$branch" + git add includes/docs-changelog.mdx + git commit -m "chore: Update docs changelog" + git push --set-upstream origin "$branch" --force + + # Check if PR already exists + existing_pr=$(gh pr list --head "$branch" --json number --jq '.[0].number') + if [ -n "$existing_pr" ]; then + echo "PR #$existing_pr already exists, updated branch" + else + gh pr create \ + --title "chore: Update docs changelog" \ + --body "Automated update of the docs changelog from recent merged PRs." \ + --label "Type: Maintenance" + gh pr merge --squash --auto + fi diff --git a/docs/changelog.mdx b/docs/changelog.mdx index 0e511c1aa26add..bf2575944b19b2 100644 --- a/docs/changelog.mdx +++ b/docs/changelog.mdx @@ -4,20 +4,5 @@ sidebar_order: 100 description: Track recent updates to Sentry docs --- -import {DocsChangelog} from 'sentry-docs/components/changelog/docsChangelog'; - - -## Recent Updates - - - -## Alternative Views - -- [Full Content Dashboard](https://sentry-content-dashboard.sentry.dev/) - View all Sentry content (blog, videos, docs, changelog) -- [RSS Feed](https://sentry-content-dashboard.sentry.dev/api/docs/feed) - Subscribe to doc updates in your RSS reader -- [JSON API](https://sentry-content-dashboard.sentry.dev/api/docs) - Programmatically access changelog data - - - The changelog updates automatically throughout the day. Summaries are generated by AI to provide quick, user-friendly insights into each update from the [getsentry/sentry-docs](https://github.com/getsentry/sentry-docs) repository. - + diff --git a/includes/docs-changelog.mdx b/includes/docs-changelog.mdx new file mode 100644 index 00000000000000..92ef011e52d24e --- /dev/null +++ b/includes/docs-changelog.mdx @@ -0,0 +1,258 @@ +## March 31, 2026 + +### [docs: add Xurrent IMR and Shiprail to third-party integrations](https://github.com/getsentry/sentry-docs/pull/17198) + +**Modified:** +- [Third-Party Integrations](/organization/integrations/third-party-integrations/) + +--- + +### [Add third-party integrations consolidated page with redirects from app](https://github.com/getsentry/sentry-docs/pull/17197) + +**Added:** +- [Third-Party Integrations](/organization/integrations/third-party-integrations/) + +**Modified:** +- [Integrations](/organization/integrations/) + +--- + +### [docs(ai): Update AI dashboards with shared includes and new AI Spans view](https://github.com/getsentry/sentry-docs/pull/17183) + +**Added:** +- [AI Agents Dashboards](/ai/monitoring/agents/dashboards/) + +**Modified:** +- [Model Costs](/ai/monitoring/agents/costs/) +- [AI Agents](/ai/monitoring/agents/) +- [Instrument AI Agents](/platforms/dotnet/tracing/instrumentation/ai-agents-module/) +- [Browser AI Monitoring](/platforms/javascript/ai-agent-monitoring-browser/) +- [Set Up AI Agent Monitoring](/platforms/javascript/guides/node/ai-agent-monitoring/) +- [Instrument AI Agents](/platforms/python/tracing/instrumentation/custom-instrumentation/ai-agents-module/) +- [Instrument AI Agents](/platforms/ruby/tracing/instrumentation/custom-instrumentation/ai-agents-module/) +- [AI Agents Dashboards](/product/dashboards/sentry-dashboards/ai/agents/) + +**Removed:** +- [/ai/monitoring/agents/dashboard/](/ai/monitoring/agents/dashboard/) + +--- + +### [docs(bun): Add bunRuntimeMetricsIntegration documentation](https://github.com/getsentry/sentry-docs/pull/17150) + +**Added:** +- [BunRuntimeMetrics](/platforms/javascript/guides/bun/configuration/integrations/bunruntimemetrics/) + +--- + +### [docs(node): Add `nodeRuntimeMetricsIntegration` documentation](https://github.com/getsentry/sentry-docs/pull/17151) + +**Added:** +- [NodeRuntimeMetrics](/platforms/javascript/guides/node/configuration/integrations/noderuntimemetrics/) + +--- + +### [Explain how to configure filters for structured logs](https://github.com/getsentry/sentry-docs/pull/16592) + +**Modified:** +- [Microsoft.Extensions.Logging](/platforms/dotnet/guides/extensions-logging/) +- [Serilog](/platforms/dotnet/guides/serilog/) + +--- + +### [docs(java): Add cache tracing integration pages](https://github.com/getsentry/sentry-docs/pull/16981) + +**Added:** +- [Caffeine Integration](/platforms/java/integrations/caffeine/) +- [Ehcache Integration](/platforms/java/integrations/ehcache/) +- [JCache Integration](/platforms/java/integrations/jcache/) +- [Redis Integration](/platforms/java/integrations/redis/) + +**Modified:** +- [Options](/platforms/java/configuration/options/) + +--- + +### [docs(ai): Add naming your agents page](https://github.com/getsentry/sentry-docs/pull/17133) + +**Added:** +- [Naming Your Agents](/ai/monitoring/agents/naming/) + +**Modified:** +- [Set Up](/ai/monitoring/agents/getting-started/) +- [AI Agents](/ai/monitoring/agents/) +- [Set Up AI Agent Monitoring](/platforms/javascript/guides/node/ai-agent-monitoring/) +- [Instrument AI Agents](/platforms/python/tracing/instrumentation/custom-instrumentation/ai-agents-module/) + +--- + +### [docs(develop-docs): Add SDK deprecation playbook](https://github.com/getsentry/sentry-docs/pull/17137) + +**Added:** +- [Deprecating an SDK](https://develop.sentry.dev/sdk/getting-started/playbooks/sdk-lifecycle/deprecating-an-sdk/) + +**Modified:** +- [Aligning Cross-SDK Changes](https://develop.sentry.dev/sdk/getting-started/playbooks/coordination/aligning-cross-sdk-changes/) +- [Introducing Breaking Changes](https://develop.sentry.dev/sdk/getting-started/playbooks/sdk-lifecycle/breaking-changes/) +- [Dropping Platform or Language Version Support](https://develop.sentry.dev/sdk/getting-started/playbooks/sdk-lifecycle/dropping-platform-support/) +- [SDK Lifecycle](https://develop.sentry.dev/sdk/getting-started/playbooks/sdk-lifecycle/) +- [API and Architecture](https://develop.sentry.dev/sdk/getting-started/standards/api-architecture/) + +--- + +### [docs(unreal): Add network performance monitoring metrics](https://github.com/getsentry/sentry-docs/pull/17167) + +**Modified:** +- [Set Up Metrics](/platforms/unreal/metrics/) + +--- + +## March 30, 2026 + +### [feat(flagpole): Update flagpole documentation with new owner field](https://github.com/getsentry/sentry-docs/pull/16918) + +**Modified:** +- [Flagpole](https://develop.sentry.dev/backend/application-domains/feature-flags/flagpole/) + +--- + +### [feat(Dashboards): Added new markdown widget docs](https://github.com/getsentry/sentry-docs/pull/17163) + +**Modified:** +- [Custom Dashboards](/product/dashboards/custom-dashboards/) +- [Widget Builder](/product/dashboards/widget-builder/) +- [Widget Library](/product/dashboards/widget-library/) + +--- + +### [add permissions details about CRUD for releases](https://github.com/getsentry/sentry-docs/pull/17034) + +**Modified:** +- [Permissions & Scopes](/api/permissions/) + +--- + +### [docs(go): Add unreleased SDK options and Scope.SetAttributes](https://github.com/getsentry/sentry-docs/pull/16789) + +**Modified:** +- [Options](/platforms/go/configuration/options/) +- [Scopes and Hubs](/platforms/go/enriching-events/scopes/) + +--- + +## March 27, 2026 + +### [feat(dashboards): Removed beta alerts on Sentry Dashboards](https://github.com/getsentry/sentry-docs/pull/17122) + +**Modified:** +- [AI Agents Dashboards](/product/dashboards/sentry-dashboards/ai/agents/) +- [AI](/product/dashboards/sentry-dashboards/ai/) +- [MCP Dashboards](/product/dashboards/sentry-dashboards/ai/mcp/) +- [Caches](/product/dashboards/sentry-dashboards/backend/caches/) +- [Backend Dashboards](/product/dashboards/sentry-dashboards/backend/) +- [Queries](/product/dashboards/sentry-dashboards/backend/queries/) +- [Queues](/product/dashboards/sentry-dashboards/backend/queues/) +- [Assets](/product/dashboards/sentry-dashboards/frontend/assets/) +- [Frontend Dashboards](/product/dashboards/sentry-dashboards/frontend/) +- [Session Health](/product/dashboards/sentry-dashboards/frontend/session-health/) +- [Web Vitals](/product/dashboards/sentry-dashboards/frontend/web-vitals/) +- [Web Vitals Concepts](/product/dashboards/sentry-dashboards/frontend/web-vitals/web-vitals-concepts/) +- [Sentry Dashboards](/product/dashboards/sentry-dashboards/) +- [Mobile Dashboards](/product/dashboards/sentry-dashboards/mobile/) +- [App Starts](/product/dashboards/sentry-dashboards/mobile/mobile-vitals/app-starts/) +- [Mobile Vitals](/product/dashboards/sentry-dashboards/mobile/mobile-vitals/) +- [Screen Loads](/product/dashboards/sentry-dashboards/mobile/mobile-vitals/screen-loads/) +- [Session Health](/product/dashboards/sentry-dashboards/mobile/session-health/) +- [Outbound API Requests](/product/dashboards/sentry-dashboards/outbound-api-requests/) +- [Transaction Summary](/product/dashboards/sentry-dashboards/transaction-summary/) + +--- + +### [docs(unreal): Sentry Crash Reporter stacktrace](https://github.com/getsentry/sentry-docs/pull/17135) + +**Modified:** +- [Crash Reporter Client](/platforms/unreal/configuration/setup-crashreporter/) + +--- + +## March 26, 2026 + +### [doc(relay): Link to relay config code reference](https://github.com/getsentry/sentry-docs/pull/17114) + +**Modified:** +- [Configuration Options](/product/relay/options/) + +--- + +## March 13, 2026 + +### [feat(Insights): Insights to dashboards EA](https://github.com/getsentry/sentry-docs/pull/16693) + +**Added:** +- [AI Agents Dashboards](/product/dashboards/sentry-dashboards/ai/agents/) +- [AI](/product/dashboards/sentry-dashboards/ai/) +- [MCP Dashboards](/product/dashboards/sentry-dashboards/ai/mcp/) +- [Caches](/product/dashboards/sentry-dashboards/backend/caches/) +- [Backend Dashboards](/product/dashboards/sentry-dashboards/backend/) +- [Queries](/product/dashboards/sentry-dashboards/backend/queries/) +- [Queues](/product/dashboards/sentry-dashboards/backend/queues/) +- [Assets](/product/dashboards/sentry-dashboards/frontend/assets/) +- [Frontend Dashboards](/product/dashboards/sentry-dashboards/frontend/) +- [Session Health](/product/dashboards/sentry-dashboards/frontend/session-health/) +- [Web Vitals](/product/dashboards/sentry-dashboards/frontend/web-vitals/) +- [Web Vitals Concepts](/product/dashboards/sentry-dashboards/frontend/web-vitals/web-vitals-concepts/) +- [Sentry Dashboards](/product/dashboards/sentry-dashboards/) +- [Mobile Dashboards](/product/dashboards/sentry-dashboards/mobile/) +- [App Starts](/product/dashboards/sentry-dashboards/mobile/mobile-vitals/app-starts/) +- [Mobile Vitals](/product/dashboards/sentry-dashboards/mobile/mobile-vitals/) +- [Screen Loads](/product/dashboards/sentry-dashboards/mobile/mobile-vitals/screen-loads/) +- [Session Health](/product/dashboards/sentry-dashboards/mobile/session-health/) +- [Outbound API Requests](/product/dashboards/sentry-dashboards/outbound-api-requests/) +- [Transaction Summary](/product/dashboards/sentry-dashboards/transaction-summary/) + +**Modified:** +- [Clustering URL Transactions](https://develop.sentry.dev/backend/application-domains/transaction-clustering/) +- [Distributed Tracing](/concepts/key-terms/tracing/distributed-tracing/) +- [Search](/concepts/search/) +- [How to Write - Getting Started](/contributing/approach/sdk-docs/write-getting-started/) +- [How to Write - Quick Start](/contributing/approach/sdk-docs/write-quick-start/) +- [Performance Metrics](/platforms/android/tracing/instrumentation/performance-metrics/) +- [Performance Metrics](/platforms/apple/tracing/instrumentation/performance-metrics/) +- [Performance Metrics](/platforms/dart/tracing/instrumentation/performance-metrics/) +- [Performance Metrics](/platforms/dart/guides/flutter/tracing/instrumentation/performance-metrics/) +- [Performance Metrics](/platforms/dotnet/tracing/instrumentation/performance-metrics/) +- [Performance Metrics](/platforms/java/tracing/instrumentation/performance-metrics/) +- [Performance Metrics](/platforms/powershell/tracing/instrumentation/performance-metrics/) +- [Sending Performance Metrics](/platforms/python/tracing/span-metrics/performance-metrics/) +- [Performance Metrics](/platforms/react-native/tracing/instrumentation/performance-metrics/) +- [Performance Metrics](/platforms/ruby/tracing/instrumentation/performance-metrics/) +- [Performance Metrics](/platforms/unity/tracing/instrumentation/performance-metrics/) +- [Custom Dashboards](/product/dashboards/custom-dashboards/) +- [Dashboards](/product/dashboards/) +- [Widget Builder](/product/dashboards/widget-builder/) +- [Widget Library](/product/dashboards/widget-library/) +- [Query Builder](/product/explore/discover-queries/query-builder/) +- [Adding Query Equations](/product/explore/discover-queries/query-builder/query-equations/) +- [Queries](/product/insights/backend/queries/) +- [Insights](/product/insights/) +- [Transaction Summary](/product/insights/overview/transaction-summary/) +- [Project Details](/product/projects/project-details/) +- [Release Details](/product/releases/release-details/) + +--- + +### [docs(api): add API deprecation policy](https://github.com/getsentry/sentry-docs/pull/16911) + +**Added:** +- [API Deprecation Policy](https://develop.sentry.dev/backend/api/deprecation-policy/) + +**Modified:** +- [Public API Checklist](https://develop.sentry.dev/backend/api/checklist/) + +--- + +### [scm: Add source code management platform documentation](https://github.com/getsentry/sentry-docs/pull/16657) + +**Added:** +- [Source Code Management Platform](https://develop.sentry.dev/backend/source-code-management-platform/) + +--- diff --git a/scripts/update-docs-changelog.mjs b/scripts/update-docs-changelog.mjs new file mode 100644 index 00000000000000..3d5c77802a27c8 --- /dev/null +++ b/scripts/update-docs-changelog.mjs @@ -0,0 +1,480 @@ +#!/usr/bin/env node + +/** + * Fetches recent merged PRs from sentry-docs and generates a CHANGELOG.md file. + * This script is run by GitHub Actions on a schedule. + * + * Usage: node scripts/update-docs-changelog.mjs + * + * Requires GITHUB_TOKEN environment variable for API access. + */ + +import fs from 'fs'; +import path from 'path'; +import {fileURLToPath} from 'url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const ROOT_DIR = path.join(__dirname, '..'); +const OUTPUT_FILE = path.join(ROOT_DIR, 'includes', 'docs-changelog.mdx'); + +// Cache for page titles extracted from frontmatter +const pageTitleCache = new Map(); + +/** + * Extract the title from a doc file's frontmatter. + * Returns the title or null if not found. + */ +function getPageTitle(filePath) { + // Only process docs and develop-docs MDX files + const isDocsFile = filePath.startsWith('docs/') && filePath.match(/\.mdx?$/); + const isDevelopDocsFile = + filePath.startsWith('develop-docs/') && filePath.match(/\.mdx?$/); + + if (!isDocsFile && !isDevelopDocsFile) { + return null; + } + + // Check cache first + if (pageTitleCache.has(filePath)) { + return pageTitleCache.get(filePath); + } + + const fullPath = path.join(ROOT_DIR, filePath); + + // Check if file exists + if (!fs.existsSync(fullPath)) { + pageTitleCache.set(filePath, null); + return null; + } + + try { + const content = fs.readFileSync(fullPath, 'utf-8'); + + // Extract frontmatter (between --- markers) + const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/); + if (!frontmatterMatch) { + pageTitleCache.set(filePath, null); + return null; + } + + // Extract title from frontmatter + const titleMatch = frontmatterMatch[1].match(/^title:\s*["']?(.+?)["']?\s*$/m); + if (titleMatch) { + const title = titleMatch[1].trim(); + pageTitleCache.set(filePath, title); + return title; + } + } catch (error) { + console.warn(` Could not read title from ${filePath}: ${error.message}`); + } + + pageTitleCache.set(filePath, null); + return null; +} + +/** + * Get the first supported guide from a file's frontmatter. + * Returns the guide name (e.g., 'bun' from 'javascript.bun') or null. + */ +function getFirstSupportedGuide(filePath) { + const fullPath = path.join(ROOT_DIR, filePath); + + if (!fs.existsSync(fullPath)) { + return null; + } + + try { + const content = fs.readFileSync(fullPath, 'utf-8'); + const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/); + if (!frontmatterMatch) { + return null; + } + + // Look for supported: array in frontmatter + const supportedMatch = frontmatterMatch[1].match( + /supported:\s*\n((?:\s+-\s+.+\n?)+)/ + ); + if (supportedMatch) { + // Get first supported platform (e.g., " - javascript.bun") + const firstSupported = supportedMatch[1].match(/^\s+-\s+(.+)/m); + if (firstSupported) { + // Extract guide from platform string (e.g., 'bun' from 'javascript.bun') + const parts = firstSupported[1].trim().split('.'); + if (parts.length >= 2) { + return parts[1]; // Return the guide name + } + } + } + } catch (error) { + // Ignore errors + } + + return null; +} + +/** + * Convert a file path to a URL path for docs. + * Handles: + * - docs/ files - docs.sentry.io URLs + * - Platform files with /common/ - uses first supported guide from frontmatter + * - develop-docs/ files - develop.sentry.dev URLs (external) + */ +function fileToUrl(filePath) { + if (!filePath.match(/\.mdx?$/)) { + return null; + } + + // Handle develop-docs files (served at develop.sentry.dev) + if (filePath.startsWith('develop-docs/')) { + let url = filePath.replace(/^develop-docs\//, '/').replace(/\.mdx?$/, '/'); + url = url.replace(/\/index\/$/, '/'); + return `https://develop.sentry.dev${url}`; + } + + // Handle docs files + if (!filePath.startsWith('docs/')) { + return null; + } + + let url = filePath.replace(/^docs\//, '/').replace(/\.mdx?$/, '/'); + + // Handle /common/ paths + if (filePath.includes('/common/')) { + // First, check if the file specifies a supported guide in frontmatter + const guide = getFirstSupportedGuide(filePath); + if (guide) { + // File specifies a guide - use /guides// path + url = url.replace('/common/', `/guides/${guide}/`); + } else { + // No specific guide - just remove /common/ (works for most platforms) + url = url.replace('/common/', '/'); + } + } + + url = url.replace(/\/index\/$/, '/'); + return url; +} + +const GITHUB_TOKEN = process.env.GITHUB_TOKEN; +const REPO_OWNER = 'getsentry'; +const REPO_NAME = 'sentry-docs'; + +// Number of PRs to fetch +const PR_LIMIT = 50; + +// PRs to exclude (bot PRs, CI updates, etc.) +const EXCLUDED_AUTHORS = ['github-actions[bot]', 'dependabot[bot]', 'getsentry-bot']; +const EXCLUDED_TITLE_PATTERNS = [ + /^Bump API schema/i, + /^chore\(deps\)/i, + /^\[automated\]/i, + /^chore: Update docs changelog/i, +]; + +async function fetchMergedPRs() { + const headers = { + Accept: 'application/vnd.github.v3+json', + 'User-Agent': 'sentry-docs-changelog', + }; + + if (GITHUB_TOKEN) { + headers['Authorization'] = `Bearer ${GITHUB_TOKEN}`; + } + + // Fetch recently merged PRs + const searchQuery = `repo:${REPO_OWNER}/${REPO_NAME} is:pr is:merged sort:updated-desc`; + const url = `https://api.github.com/search/issues?q=${encodeURIComponent(searchQuery)}&per_page=${PR_LIMIT}`; + + console.log('Fetching merged PRs...'); + + const response = await fetch(url, {headers}); + + if (!response.ok) { + throw new Error(`GitHub API error: ${response.status} ${response.statusText}`); + } + + const data = await response.json(); + console.log(`Found ${data.items.length} merged PRs`); + + return data.items; +} + +async function fetchPRDetails(prNumber) { + const headers = { + Accept: 'application/vnd.github.v3+json', + 'User-Agent': 'sentry-docs-changelog', + }; + + if (GITHUB_TOKEN) { + headers['Authorization'] = `Bearer ${GITHUB_TOKEN}`; + } + + const url = `https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/pulls/${prNumber}`; + const response = await fetch(url, {headers}); + + if (!response.ok) { + console.warn(`Failed to fetch PR #${prNumber}: ${response.status}`); + return null; + } + + return response.json(); +} + +async function fetchPRFiles(prNumber) { + const headers = { + Accept: 'application/vnd.github.v3+json', + 'User-Agent': 'sentry-docs-changelog', + }; + + if (GITHUB_TOKEN) { + headers['Authorization'] = `Bearer ${GITHUB_TOKEN}`; + } + + const url = `https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/pulls/${prNumber}/files?per_page=100`; + const response = await fetch(url, {headers}); + + if (!response.ok) { + console.warn(`Failed to fetch files for PR #${prNumber}: ${response.status}`); + return {files: [], truncated: false}; + } + + const files = await response.json(); + + // Check if there are more pages (GitHub returns Link header for pagination) + // If we got exactly 100 files, there may be more - mark as truncated + const truncated = files.length >= 100; + + return {files, truncated}; +} + +function shouldIncludePR(pr) { + // Exclude bot authors + if (EXCLUDED_AUTHORS.includes(pr.user?.login)) { + return false; + } + + // Exclude PRs matching excluded title patterns + for (const pattern of EXCLUDED_TITLE_PATTERNS) { + if (pattern.test(pr.title)) { + return false; + } + } + + return true; +} + +function categorizeFiles(files) { + const added = []; + const modified = []; + const removed = []; + + for (const file of files) { + // Only include MDX doc files from docs/ or develop-docs/ + const isDocsFile = + file.filename.startsWith('docs/') && file.filename.match(/\.mdx?$/); + const isDevelopDocsFile = + file.filename.startsWith('develop-docs/') && file.filename.match(/\.mdx?$/); + + if (!isDocsFile && !isDevelopDocsFile) { + continue; + } + + // For non-removed files, only include if the file exists locally + // This avoids linking to pages that haven't been deployed yet + if (file.status !== 'removed') { + const fullPath = path.join(ROOT_DIR, file.filename); + if (!fs.existsSync(fullPath)) { + continue; + } + } + + const fileInfo = { + path: file.filename, + title: getPageTitle(file.filename), + url: fileToUrl(file.filename), + }; + + if (file.status === 'added') { + added.push(fileInfo); + } else if (file.status === 'removed') { + removed.push(fileInfo); + } else { + modified.push(fileInfo); + } + } + + return {added, modified, removed}; +} + +/** + * Format a file entry as a markdown list item with link + */ +function formatFileLink(file) { + if (file.url && file.title) { + return `[${file.title}](${file.url})`; + } else if (file.url) { + // Use URL path as title if no title found + return `[${file.url}](${file.url})`; + } else if (file.title) { + return file.title; + } + // Fallback to filename + return file.path.replace(/^docs\//, '').replace(/\.mdx?$/, ''); +} + +/** + * Generate markdown for a single PR entry + */ +function generatePRMarkdown(entry) { + const lines = []; + + // PR title with link + lines.push(`### [${entry.title}](${entry.url})`); + lines.push(''); + + // Files changed + const {added, modified, removed} = entry.filesChanged; + + if (added.length > 0) { + lines.push('**Added:**'); + for (const file of added) { + lines.push(`- ${formatFileLink(file)}`); + } + lines.push(''); + } + + if (modified.length > 0) { + lines.push('**Modified:**'); + for (const file of modified) { + lines.push(`- ${formatFileLink(file)}`); + } + lines.push(''); + } + + if (removed.length > 0) { + lines.push('**Removed:**'); + for (const file of removed) { + lines.push(`- ${formatFileLink(file)}`); + } + lines.push(''); + } + + return lines.join('\n'); +} + +/** + * Generate the full changelog markdown + */ +function generateChangelog(entries) { + const lines = []; + + // Group entries by date + const entriesByDate = new Map(); + for (const entry of entries) { + const date = new Date(entry.publishedAt).toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric', + }); + if (!entriesByDate.has(date)) { + entriesByDate.set(date, []); + } + entriesByDate.get(date).push(entry); + } + + // Generate markdown for each date + for (const [date, dateEntries] of entriesByDate) { + lines.push(`## ${date}`); + lines.push(''); + + for (const entry of dateEntries) { + lines.push(generatePRMarkdown(entry)); + lines.push('---'); + lines.push(''); + } + } + + return lines.join('\n'); +} + +async function main() { + console.log('Starting docs changelog update...'); + + if (!GITHUB_TOKEN) { + console.warn('Warning: GITHUB_TOKEN not set. API rate limits will be restrictive.'); + } + + try { + const prs = await fetchMergedPRs(); + const entries = []; + + for (const pr of prs) { + if (!shouldIncludePR(pr)) { + console.log(`Skipping PR #${pr.number}: ${pr.title}`); + continue; + } + + console.log(`Processing PR #${pr.number}: ${pr.title}`); + + // Fetch additional PR details + const prDetails = await fetchPRDetails(pr.number); + if (!prDetails) continue; + + // Fetch files changed + const {files: prFiles, truncated} = await fetchPRFiles(pr.number); + + // Skip PRs with 100+ files to avoid incomplete data + // (we fetch 50 PRs but only need 20 entries, so we have buffer) + if (truncated) { + console.log(` Skipping - PR has 100+ files, likely a large refactor`); + continue; + } + + const filesChanged = categorizeFiles(prFiles); + + // Skip PRs with no doc file changes + const totalDocFiles = + filesChanged.added.length + + filesChanged.modified.length + + filesChanged.removed.length; + if (totalDocFiles === 0) { + console.log(` Skipping - no documentation files changed`); + continue; + } + + const entry = { + title: String(pr.title || ''), + url: String(pr.html_url || ''), + publishedAt: String(prDetails.merged_at || pr.closed_at || pr.updated_at || ''), + author: String(pr.user?.login || 'unknown'), + filesChanged, + }; + + entries.push(entry); + console.log(` Added: ${entry.title} (${totalDocFiles} doc files)`); + + // Small delay to avoid rate limiting + await new Promise(resolve => setTimeout(resolve, 100)); + } + + // Sort by date, most recent first + entries.sort( + (a, b) => new Date(b.publishedAt).getTime() - new Date(a.publishedAt).getTime() + ); + + // Limit to 20 most recent + const recentEntries = entries.slice(0, 20); + + // Generate markdown + const markdown = generateChangelog(recentEntries); + + // Write to file + fs.writeFileSync(OUTPUT_FILE, markdown); + console.log(`\nWrote ${recentEntries.length} entries to ${OUTPUT_FILE}`); + } catch (error) { + console.error('Error updating changelog:', error); + process.exit(1); + } +} + +main(); diff --git a/src/components/changelog/docsChangelog.tsx b/src/components/changelog/docsChangelog.tsx deleted file mode 100644 index b521daacd66dd3..00000000000000 --- a/src/components/changelog/docsChangelog.tsx +++ /dev/null @@ -1,174 +0,0 @@ -interface ChangelogEntry { - author: string; - description: string; - id: string; - publishedAt: string; - title: string; - url: string; - filesChanged?: { - added: string[]; - modified: string[]; - removed: string[]; - }; -} - -async function getChangelogEntries(): Promise { - try { - const res = await fetch('https://sentry-content-dashboard.sentry.dev/api/docs', { - next: {revalidate: 3600}, // Cache for 1 hour - }); - - if (!res.ok) { - throw new Error(`Failed to fetch changelog: ${res.status} ${res.statusText}`); - } - - return res.json(); - } catch (error) { - console.error('Error fetching changelog:', error); - // Error fetching changelog - return empty array - return []; - } -} - -export async function DocsChangelog() { - const entries = await getChangelogEntries(); - - if (entries.length === 0) { - return ( -
-

No changelog entries available

-

Check back later for updates.

-
- ); - } - - // Show only the 20 most recent entries - const recentEntries = entries.slice(0, 20); - - return ( -
- {recentEntries.map(entry => { - const date = new Date(entry.publishedAt); - const totalFiles = - (entry.filesChanged?.added?.length || 0) + - (entry.filesChanged?.modified?.length || 0) + - (entry.filesChanged?.removed?.length || 0); - - return ( -
-
-

- - {entry.title.replace('Docs Update: ', '')} - -

-
- - {totalFiles > 0 && } - {totalFiles > 0 && ( - - {totalFiles} file{totalFiles !== 1 ? 's' : ''} changed - - )} -
-
- -

- {entry.description} -

- - {entry.filesChanged && totalFiles > 0 && ( -
- - View changed files - -
- {entry.filesChanged.added && entry.filesChanged.added.length > 0 && ( -
- - Added: - -
    - {entry.filesChanged.added.map(file => ( -
  • - {file} -
  • - ))} -
-
- )} - {entry.filesChanged.modified && - entry.filesChanged.modified.length > 0 && ( -
- - Modified: - -
    - {entry.filesChanged.modified.map(file => ( -
  • - {file} -
  • - ))} -
-
- )} - {entry.filesChanged.removed && - entry.filesChanged.removed.length > 0 && ( -
- - Removed: - -
    - {entry.filesChanged.removed.map(file => ( -
  • - {file} -
  • - ))} -
-
- )} -
-
- )} -
- ); - })} - {entries.length > 20 && ( -
-

- Showing the 20 most recent updates. View the{' '} - - full content dashboard - {' '} - or subscribe to the{' '} - - RSS feed - - . -

-
- )} -
- ); -} diff --git a/src/components/mobileMenu/index.tsx b/src/components/mobileMenu/index.tsx index b48e743e555f1e..509cf25f34be9d 100644 --- a/src/components/mobileMenu/index.tsx +++ b/src/components/mobileMenu/index.tsx @@ -48,7 +48,7 @@ export function MobileMenu({pathname, searchPlatforms}: Props) { ))}
  • - Changelog + Product Changelog
  • Sandbox diff --git a/src/components/navigationData.ts b/src/components/navigationData.ts index c15e361c5ba652..76529e376a1e09 100644 --- a/src/components/navigationData.ts +++ b/src/components/navigationData.ts @@ -23,6 +23,7 @@ export const moreSections: NavSection[] = [ {label: 'Organization Settings', href: '/organization/'}, {label: 'Pricing & Billing', href: '/pricing/'}, {label: 'Security, Legal, & PII', href: '/security-legal-pii/'}, + {label: 'Docs Changelog', href: '/changelog/'}, ]; // Main navigation sections with dropdowns (used in TopNavClient) @@ -50,4 +51,5 @@ export const mainSections: NavSection[] = [ {label: 'Organization Settings', href: '/organization/'}, {label: 'Pricing & Billing', href: '/pricing/'}, {label: 'Security, Legal, & PII', href: '/security-legal-pii/'}, + {label: 'Docs Changelog', href: '/changelog/'}, ]; diff --git a/src/components/sidebar/SidebarMoreLinks.tsx b/src/components/sidebar/SidebarMoreLinks.tsx index 6e3aa40f22323e..8afdb37a2c8127 100644 --- a/src/components/sidebar/SidebarMoreLinks.tsx +++ b/src/components/sidebar/SidebarMoreLinks.tsx @@ -16,7 +16,7 @@ export function SidebarMoreLinks() { {/* Always visible links */}