Source code for ddev.com’s static front end, built with Astro to keep things organized, maintainable, and fast.
The file structure follows a typical Astro project layout.
Most pages are built with Astro components, while blog posts and authors are sourced from local Markdown that’s validated with tidy schemas we get using content collections.
cache/
– custom, project-specific folder for caching GitHub responses in local developent to reduce API calls.public/
– images and redirects that will be copied verbatim into the generated dist/
directory.src/
– components, layouts, styles, and supporting TypeScript/JavaScript.
components/
– individual .astro
components used in pages. (You can also use components for UI frameworks like Vue, React, and Svelte!)content/
– configuration and Markdown for the blog’s content collections.layouts/
– contains the single component we use for every page.lib/
– helper code for fetching data from GitHub, building the search index, injecting read time into frontmatter, and handling common formatting.pages/
– .astro
pages whose filenames directly translate into routes for the site.styles/
– global PostCSS that’s not already handled by the Tailwind plugin..env.example
– file you’ll want to rename .env
and populate for a new environment..nvmrc
– Node.js version to support nvm use
..prettierrc
– rules for Prettier code formatting.astro.config.mjs
– Astro configuration.package.json
– standard file that details the project’s packages and versions.README.md
– you are here! 👋tailwind.config.cjs
– configuration for Tailwind and the Tailwind Typography plugin we’re using.tsconfig.json
– TypeScript configuration.All commands are run from the root of the project, from a terminal:
Command | Action |
---|---|
npm install |
Installs dependencies |
npm run dev |
Starts local dev server at localhost:3000 |
npm run build |
Build your production site to ./dist/ |
npm run preview |
Preview your build locally, before deploying |
npm run astro ... |
Run CLI commands like astro add , astro preview |
npm run astro --help |
Get help using the Astro CLI |
npm run textlint |
Run textlint on content collections |
npm run textlint:fix |
Apply fixable updates to resolve textlint errors |
DDEV already has all the dependencies included.
ddev start
to start and set up the project’s dependencies.To rebuild a static copy of the site, run ddev npm run build
. The contents of the dist/
folder are what gets deployed to Cloudflare Pages and can be found at https://
Troubleshooting steps: Check ddev logs
.
Check out the project in your favorite Node.js environment, ideally running nvm
. We’ll install dependencies, add a GitHub API key, and run a local dev server with a hot-reloading browser URL.
nvm use
to make sure you’re running an appropriate Node.js version.npm install
to set up the project’s dependencies.npm run dev
to start Astro’s dev server. If it fails then run npm cache clean --force && npm install && npm run dev
. http://localhost:4321/
.) The site will automatically refresh as you work on it, displaying errors in the relevant terminal or browser console.To generate a static copy of the site, run npm run build
. The contents of the dist/
folder are exactly what get deployed to Cloudflare Pages. You can preview locally by running npm run preview
or using a tool like serve
.
Make sure to delete your node_modules/
directory and run ddev npm install
. The change in architecture can create odd issues otherwise.
This step is not required if you just want to contribute a blog post to ddev.com.
Contributors, sponsors, releases and more data about DDEV is retrieved dynamically from the GitHub API. To test this, please follow these steps:
cp .env.example .env
to create a .env
file for environment variables. (Don’t check this in!)repo
, read:org
, read:user
, and read:project
..env
’s GITHUB_TOKEN=
.There is a local cache/
to reduce API calls.
The site’s content lives in either .astro
components that resemble souped-up HTML, or Markdown files organized into schema-validated content collections.
Blog posts are Markdown files with frontmatter that live in src/content/blog/
.
To add a new blog post, use this Markdown as a template:
---
title: "It’s A Post!"
pubDate: 2023-01-01
summary:
author: Randy Fay
featureImage:
src: /img/blog/kebab-case.jpg
alt:
caption:
credit:
categories:
- DevOps
---
Name your file with a kebab-case, URL-and-SEO-friendly slug with a .md
extension, and drop it in the src/content/blog/
directory.
Give it a succinct title, and if you include a feature image be sure to write descriptive alt text along with an optional caption and image credit. The caption:
and credit:
fields can both use Markdown, but you’ll probably need to wrap the whole value in straight quotes ("
).
The Astro build doesn’t do any fancy image sizing or optimization, so be sure any images you add are production-ready: an appropriate format for the image type (JPEG, PNG, or SVG), with size no larger than ~1–2MB and dimensions no greater than 2000px or so. Use an app like ImageOptim to quickly apply lossless compression.
Choose whichever categories apply, with special attention to the first because it’ll be displayed on post summary cards:
💡 If you’re publishing work from a new author, add an entry for them in
src/content/authors/
! The"name"
value needs to match the one you’re using in your post frontmatter.
Add a .astro
file to the pages/
directory, where its name will become the page slug. Use an existing page to grab and re-use whatever layout and components you can to save yourself time and encourage consistency with the rest of the site.
If you need to dynamically add multiple pages, see files with brackets like src/blog/[page].astro
, src/blog/category/[slug].astro
, and src/blog/author/[slug].astro
for examples.
A basic textlint configuration lives in .textlintrc
and runs against src/content/**
to try and help keep language consistent and accurate. This doesn’t yet conform to the DDEV docs spellcheck rules and massive exclusion list, but ideally the two can someday converge.
Textlint’s default terminology catches a lot of accepted best practices on its own, where the only major override is to allow “website” (instead of its suggested “site”) because it’s rampant in blog posts and documentation. Same with the “front end” and “back end” conundrum and two-word “command line”.
Run npm run textlint
to check everything, and you can apply “fixable” changes using npm run textlint:fix
. Be careful automating fixes to be sure they don’t have any unintended side effects!
The src/featured-sponsors.json
file is used for manually curating prominent sponsors.
While it’s a bit of a pain and still relies on coercion in some places, it lets us collect pristine, brand-friendly resources in one place and use them in different contexts.
It’s used to display sponsor details in a few places:
If you’re adding a new item to the array, choose whichever position it should appear in and use the following format:
{
"name": "Platform.sh",
"type": "major",
"logo": "/logos/platform.sh.svg",
"squareLogo": "/logos/platform.sh-square.svg",
"url": "https://platform.sh",
"github": "platformsh",
},
"major"
or "standard"
depending on contribution level. (Not currently used but can affect styling later.)public/logos/
directory. Make sure this is a clean, optimized vector SVG file unless it’s a person’s headshot. (Again, follow the organization’s brand guide wherever possible!)logo
is already square.Any redirect can be added to ddev.com by editing public/_redirects
. This can be useful to provide short redirects in a variety of contexts. Redirects can be to local URLs or to DDEV docs, for example.
301
for a permanent redirect./s
to imply their nature. For example, /s/port-conflict
For the site to exist at ddev.com
, it needs to be built and hosted somewhere. Cloudflare Pages responds to commits in order to build and deploy the site.
On every push to the main
branch, the following happens:
npm run build
, and deploys the resulting output from dist/
.
The site uses Octokit to make REST and GraphQL API requests for repository and contribution details from github.com. It needs an API token to authenticate these requests to function and avoid hitting quota limits.
GitHub supplies its own private GITHUB_TOKEN
in the GitHub Actions build environment. In any other environment, including local development, you’ll need to populate a GITHUB_TOKEN
environment variable with a classic GitHub personal access token that has repo
, read:org
, read:user
, and read:project
scopes.
A valid Personal Access Token (PAT) must also be supplied to Cloudflare.