Replace static DDEV usage-stats post with a live Amplitude page, fixes #671#672
Conversation
…d page,fixes #671 Adds src/pages/usage-stats.astro, fetching live chart data from Amplitude's Dashboard REST API and ad hoc Events Segmentation API at build time (src/lib/amplitude.ts), following the same pattern src/lib/api.ts already uses for GitHub stats: env-var-gated, cached to disk, gracefully degrades when credentials aren't configured. Fixes a real bug found along the way: the saved "Usage by Architecture" chart had no OS filter at all, silently mixing Linux/Windows amd64 hosts into what was supposed to be a macOS-only Apple Silicon vs. Intel split. Replaced with a properly darwin-filtered query. Removes the obsolete nov-2024 post and its exclusive images (301 redirect added), fixes the two posts that linked to it, and adds a modifiedDate to the Amplitude contributor-training post pointing at the new page. Wires AMPLITUDE_API_KEY/AMPLITUDE_SECRET_KEY into test.yml via the existing 1Password service-account pattern (matching CF_API_TOKEN's setup) so PR builds exercise the live fetch path; skipped for forked PRs since GitHub Actions doesn't expose secrets to those anyway. Fixes #671 🤖 Developed with assistance from [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Deploying ddev-com-front-end with
|
| Latest commit: |
8c7b6c4
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://d76f7f87.ddev-com-front-end.pages.dev |
| Branch Preview URL: | https://20260715-usage-stats-amplitu.ddev-com-front-end.pages.dev |
stasadev
left a comment
There was a problem hiding this comment.
Wow, this looks great!
I see some issues with image labels in dark mode, the text is black and unreadable - I'll take a look.
… CTA Filters out rows with 0 usage in the selected period (toPercentageRows) — these were previously-active entries that just happened to be zero in whichever single column got picked, not genuinely unused. Also excludes known-stale values that shouldn't appear: WSL2 Docker Providers "(none)", and the long-abandoned "python"/"django4" CMS project types. Expands CMS Project Types to show all types (was capped at top 8) and DDEV Commands to show 18 (was 8) — verified the additional commands are all recognized official subcommands, not leaked custom command names. Adds a full, dynamically-updated add-on usage list as a foldable <details> section on the main page (no separate page, no JS), plus a CSV download endpoint at /usage-stats/add-ons.csv. Drops the percentage column from the add-on list per feedback that it isn't useful there. Adds a sponsor CTA at the bottom of the page. 🤖 Developed with assistance from [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Pulls monthly sponsorship income from ddev/sponsorship-data's daily history snapshots (public, no credentials needed) and plots it with the same trend chart used for usage growth. Dynamically pulls the current sponsorship goal (target amount and progress percentage) from the live sponsorship-data feed already used elsewhere on the site, both in the section's prose and as a marked goal line on the chart, so it stays correct as the goal or progress changes. Generalizes UsageTrendChart with optional ariaLabel/tooltipUnit/ valuePrefix/goalValue/goalLabel props (defaults unchanged) so the same component works for dollars as well as user counts. 🤖 Developed with assistance from [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
…e Type sections Adds a SectionHeading component that gives every section on /usage-stats a stable #slug anchor, matching the self-link pattern blog post headings already get from rehype-autolink-headings (which doesn't apply here since this page isn't markdown-rendered). Adds PHP Version and Database Type breakdowns, sourced from Amplitude's "Project" event properties. These needed a different ad hoc query shape than the rest of the page: properties scoped to a single event type require an inline group_by in the event definition rather than the top-level `g` param used for the macOS-architecture query, which only works for Amplitude's built-in cross-event properties. Generalizes getMacosArchitecture's return type (renamed ArchitectureBreakdown -> PropertyBreakdown) and factors out the shared query/parse logic into getProjectPropertyBreakdown(). 🤖 Developed with assistance from [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
PHP Version was capped at UsageStatBars' default limit of 8, hiding the long tail. Also excludes "(none)" (not a real PHP version choice, same treatment as Database Type and every other "(none)" row on this page) which had started showing up once the section was uncapped. 🤖 Developed with assistance from [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
| --- | ||
|
|
||
| <h2 id={slug} class="text-2xl font-bold dark:text-white scroll-mt-24"> | ||
| <a href={`#${slug}`} class="hover:underline"> |
There was a problem hiding this comment.
We don't use hover:underline in the blogs.
I.e. https://ddev.com/blog/ddev-xdebug-quickstart-phpstorm/#watch-the-video
We need a consistency: either to have it everywhere or not to use it at all.
| const environment = await getAmplitudeChart(AMPLITUDE_CHARTS.environment) | ||
| const commands = await getAmplitudeChart(AMPLITUDE_CHARTS.commands) | ||
| const cmsProjectTypes = await getAmplitudeChart( | ||
| AMPLITUDE_CHARTS.cmsProjectTypes | ||
| ) | ||
| const macosDockerProviders = await getAmplitudeChart( | ||
| AMPLITUDE_CHARTS.macosDockerProviders | ||
| ) | ||
| const wsl2DockerProviders = await getAmplitudeChart( | ||
| AMPLITUDE_CHARTS.wsl2DockerProviders | ||
| ) | ||
| const macosArchitecture = await getMacosArchitecture() | ||
| const phpVersions = await getPhpVersions() | ||
| const databaseTypes = await getDatabaseTypes() | ||
| const addOns = await getAmplitudeChart(AMPLITUDE_CHARTS.addOns) | ||
| const ddevPull = await getAmplitudeChart(AMPLITUDE_CHARTS.ddevPull) | ||
| const versions = await getAmplitudeChart(AMPLITUDE_CHARTS.versions) | ||
| const timezones = await getAmplitudeChart(AMPLITUDE_CHARTS.timezones) |
There was a problem hiding this comment.
There's some inconsistency between using getAmplitudeChart and API directly.
tyler36
left a comment
There was a problem hiding this comment.
Looks good.
We need to be consistent with paragraphs though (#672 (comment))
Blog post heading-anchors (via rehype-autolink-headings) render as plain self-links with no hover state; SectionHeading added one that wasn't part of that pattern. Addresses @stasadev's review comment on PR #672: #672 (comment) 🤖 Developed with assistance from [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Unlike /privacy's hand-maintained "Last updated" date, this is a build timestamp — this page is regenerated from live data on every build rather than manually edited, so it reflects when the numbers below were actually pulled rather than when someone last touched the page. Addresses @stasadev's review comment on PR #672: #672 (comment) 🤖 Developed with assistance from [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
The intro paragraph and "Explore them yourself!" block were the only two sections wrapped in PostBody, which applies Tailwind Typography's .prose class. This project doesn't override Typography's default max-width: 65ch, so those two blocks rendered visibly narrower than every other section on the page, which sits directly in the max-w-4xl container with no such cap. Switching them to the same plain markup (and linkClass for their links, since .prose was also supplying dark-mode link styling that plain markup doesn't) as the rest of the page fixes both the width mismatch and the paragraph style/color inconsistency in one move. Addresses @tyler36's review (CHANGES_REQUESTED) and comment on PR #672: #672 (comment) 🤖 Developed with assistance from [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
getAmplitudeChart() exports a saved chart's CSV as-is; getMacosArchitecture(), getPhpVersions(), getDatabaseTypes(), and getMonthlyUserHistory() build custom Events Segmentation queries instead. That's intentional rather than inconsistent — each ad hoc one exists because no saved chart covers that property/filter/date-range correctly (details in each function's own comment in lib/amplitude.ts) — but the split wasn't called out at the call sites themselves. Addresses @stasadev's review comment on PR #672: #672 (comment) 🤖 Developed with assistance from [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
tyler36
left a comment
There was a problem hiding this comment.
Confirmed paragraphs all display as full-width.
| // linuxDockerProviders: | ||
| // "https://app.amplitude.com/analytics/share/REPLACE_ME", |
There was a problem hiding this comment.
@rfay, I have no idea how to share this chart https://app.amplitude.com/analytics/ddev/chart/39z7c3sh
There's no share button available:
There was a problem hiding this comment.
https://amplitude.com/docs/analytics/share-external#copy-edit-or-delete-a-public-link
The docs explain how to share it, but I don't have any of those options, even though I have admin privileges.
Either it's not working as expected, or I don't have the required permissions.
There was a problem hiding this comment.
I can't guess unless they have taken away the feature or they're having a problem with it right now. I don't find any access to it. It's possible we should remove teh "live link" from existing items.
stasadev
left a comment
There was a problem hiding this comment.
Looks good to me.
I added pie charts, UTC offsets and Linux providers (without a share link).
|
Awesome. We can improve this as time goes on. |



Summary
src/pages/usage-stats.astro, fetching live chart data from Amplitude's Dashboard REST API and ad hoc Events Segmentation API at build time (src/lib/amplitude.ts), following the same env-var-gated, disk-cached, gracefully-degrading patternsrc/lib/api.tsalready uses for GitHub stats.stats-on-ddev-usage-nov-2024post and its exclusive images, adds a 301 redirect, and fixes the two posts that linked to it.modifiedDate/modifiedCommentpointing at the new live page.AMPLITUDE_API_KEY/AMPLITUDE_SECRET_KEYintotest.ymlvia the existing 1Password service-account pattern (matchingCF_API_TOKEN's setup), so PR builds exercise the live fetch path; skipped for forked PRs since GitHub Actions doesn't expose secrets to those anyway.Test plan
Review at https://20260715-usage-stats-amplitu.ddev-com-front-end.pages.dev/usage-stats/
And https://20260715-usage-stats-amplitu.ddev-com-front-end.pages.dev/about/
ddev npm run buildsucceeds and renders all sections with live dataddev prettier/ddev textlintcleanAMPLITUDE_API_KEY/AMPLITUDE_SECRET_KEYneed to be added to Cloudflare Pages' production environment variables before this will show live data in production (falls back to a "not configured" notice otherwise)🤖 Generated with Claude Code