This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is a Swift-based static site generator for the BrightDigit website using the Publish framework. The project includes multiple Swift modules for content management, podcast integration, newsletter automation, and website generation.
# Build the project
swift build
# Run tests
swift test
# Build for release
swift build -c release --product brightdigitwg
# Run the main executable (defaults to publish command)
swift run brightdigitwg --mode production
swift run brightdigitwg --mode drafts# Import content from external sources
swift run brightdigitwg import mailchimp --mailchimp-api-key=<key> --mailchimp-list-id=<id> --export-markdown-directory=Content/newsletters
swift run brightdigitwg import podcast --youtube-api-key=<key> --export-markdown-directory Content/episodes
swift run brightdigitwg import wordpress --wordpress-url=<url> --export-markdown-directory=Content/articles
# Publish content (--mode is required)
swift run brightdigitwg publish --mode production
swift run brightdigitwg publish --mode drafts# SwiftLint and SwiftFormat configurations exist but are not actively enforced in CI
# .swiftlint.yml - relaxed limits for content-heavy project (line length: 150, file length: 550)
# .swiftformat - code formatting rules-
Strict concurrency is mandatory. Every Swift target enables complete strict concurrency checking (
swift-tools-version:6.4, Swift 6 language mode, i.e.-strict-concurrency=complete). When a strict-concurrency error surfaces, fix it properly (addSendable/@Sendable, isolate with actors/@MainActor, restructure ownership) — do not lower the language mode, relax the setting, or silence the diagnostic to make it build. -
Resolve module-name collisions in the call site, not by renaming our own types. When two modules export the same symbol (e.g. swift-markdown's
Markdownvs. Publish'sMarkdown), disambiguate with — in order of preference — SwiftPMmoduleAliases:, the Swift 6.4 module selector (Module::Symbol), atypealias, or a selectiveimport struct Module.Symbol. Never work around a collision by renaming our own first-party type.
- brightdigitwg - Main executable entry point (located in executable target)
- BrightDigitArgs - Command-line interface using ArgumentParser with three subcommands:
publish(default) - Generates the static site with production/drafts modesimport- Imports content from Mailchimp, YouTube, WordPress, or podcast RSSurl- URL utilities
- BrightDigitSite - Main site generation logic with custom publishing pipeline
- Defines
SectionIDenum for content types (articles, episodes, tutorials, newsletters, products) - Configures publishing steps (markdown processing, RSS generation, sitemap, npm build)
- Implements two modes: drafts (includes future-dated content) vs production (filters by date)
- Defines
- PublishType - Type-safe abstractions for Publish framework
SectionBuilder- Type-safe section page generationPageBuilder- Dynamic page content systemContentBuilder- Reusable content construction patterns
- PiHTMLFactory - Custom HTML factory implementing the site's theme
- Generates HTML for index, sections, items, and pages
- Integrates with Plot DSL for type-safe HTML generation
- ContributeMailchimp - Mailchimp newsletter import and markdown generation
- ContributeYouTube - YouTube content integration via SwiftTube
- ContributeRSS - RSS feed processing via SyndiKit
- BrightDigitPodcast - Podcast episode management combining YouTube and RSS
- Tagscriber - Markdown generation from web content using Kanna
Content/- Source markdown files for site contentarticles/- Blog articlesnewsletters/- Newsletter content (auto-generated from Mailchimp)episodes/- Podcast episodes (auto-generated)tutorials/- Tutorial content
Sources/- Swift source code modulesTests/- Unit tests
- Publish - Static site generation framework by John Sundell
- Plot - Type-safe HTML DSL for Swift
- SwiftTube - YouTube Data API v3 integration
- SyndiKit - RSS/Atom feed parsing (used for podcast import)
- Spinetail - Mailchimp API client (for newsletter import)
- Kanna - HTML/XML parsing (used by Tagscriber for web content extraction)
- ArgumentParser - Apple's Swift Argument Parser for CLI
- MarkdownGenerator - Markdown document generation
- Publish Plugins: SplashPublishPlugin, YoutubePublishPlugin, ReadingTimePublishPlugin, TransistorPublishPlugin, NPMPublishPlugin
CI/CD runs on GitHub Actions (.github/workflows/main.yaml). All Linux jobs run in the brightdigit/publish-xml:6.4 container (Swift 6.4, Ubuntu Noble — based on the swiftlang/swift:nightly-6.4.x-noble snapshot), built from this repo's Dockerfile. Jobs:
-
automate-content - Manual/dispatch job (self-hosted macOS) that imports content from Mailchimp and YouTube, then commits and pushes any new content. Triggered via the
AUTOMATE_CONTENTworkflow input. -
build-linux - Runs
swift buildandswift testin the container. Caches.build/with a key that embeds the Swift toolchain version (swift --version), so a toolchain change auto-invalidates the cache. -
package-linux - Builds the release binary (
swift build -c release --product brightdigitwg) and uploads it as an artifact (brightdigitwg-Linux-x86_64). -
deploy - Downloads the artifact, generates the site (
--mode productiononmain,--mode draftsotherwise), and deploys to Netlify (--prodonmain). RequiresNETLIFY_AUTH_TOKENandNETLIFY_PRODUCTION_SITE_ID.
The self-hosted macOS build/package jobs are currently commented out in the workflow. The Docker image is bumped by editing Dockerfile and pushing brightdigit/publish-xml:6.4 (+ :latest). Swift 6.4 is currently a pre-release nightly — the image is based on swiftlang/swift:nightly-6.4.x-noble. Since 6.4 is not yet released, .swift-version pins the 6.4.x-snapshot toolchain (install with swiftly install 6.4.x-snapshot); the Xcode 6.4 toolchain (xcrun swift) also works for local builds.
- Tests are located in
Tests/BrightDigitSiteTests/ - Run tests:
swift test - Project requires Swift 6.4+ and macOS 13+
- Linux builds use Ubuntu Noble (24.04) with custom Docker image
- Swift Package Manager handles all dependency resolution
- Site generation uses a multi-step pipeline defined in
BrightDigitSite.swift- Pre-markdown: Copy resources, install plugins, add markdown files
- Post-markdown: Fix YAML, calculate reading time, sort items, generate HTML/RSS/sitemap
- Final step: NPM build process for styling (requires
NPM_PATHenvironment variable)
- Two publishing modes control content visibility:
production- Filters out future-dated content (items wheredate > now)drafts- Includes all content including future-dated items
- All content importers use the
Contributeframework for markdown generation - Each importer has three components:
Source- API client/data fetcherFrontMatterTranslator- Converts API data to front matterMarkdownExtractor- Generates markdown body content
- Tagscriber module can extract markdown from arbitrary web URLs using Kanna
- Custom
PiHTMLFactoryimplements all HTML generation using Plot DSL PublishTypeframework provides type-safe abstractions for sections and pages- Each section type (articles, episodes, tutorials, newsletters) has its own
SectionItemimplementation
- Requires Node.js/NPM for final styling build step via NPMPublishPlugin
- GitHub Actions caches the
.build/directory keyed on the Swift toolchain version +Package.resolvedfor faster builds