cms pipeline
what is this?
content lives in a private repo (lib) for local editing with obsidian/neovim. on push, a github action syncs it to the public site repo and triggers a deploy to cloudflare pages.
no database. no cms dashboard. just markdown and git.
the flow
local edit -> push to lib -> webhook -> sooriya action -> build -> cloudflare pages | | | neovim/obsidian repository_dispatch rsync contentcontent sync action
the action lives in .github/actions/content-sync/action.yml. it’s a composite action that:
- clones private content repo
- rsyncs to
src/content/ - validates structure
clone step
- name: clone private content repository shell: bash env: GITHUB_TOKEN: ${{ inputs.token }} REPO_URL: ${{ inputs.repo-url }} run: | git config --global url."https://${GITHUB_TOKEN}@github.com/".insteadOf "https://github.com/" git clone --depth 1 "${REPO_URL}" /tmp/content-repouses a fine-grained pat with read access to the private repo. stored as CONTENT_REPO_TOKEN secret.
sync step
- name: sync content to destination shell: bash run: | rsync -av --delete \ --exclude='.git' \ --exclude='.github' \ --exclude='README.md' \ --exclude='tmp/' \ --exclude='lists/' \ --exclude='.DS_Store' \ /tmp/content-repo/ "${CONTENT_DIR}/"--delete ensures removed files get deleted on the site too. excludes filter out non-content stuff.
validation
counts files per collection and warns if something looks wrong:
for dir in posts tweets pages proj; do if [ ! -d "${CONTENT_DIR}/${dir}" ]; then echo "warning: ${dir} directory not found" fidonedeploy workflow
the main workflow in .github/workflows/deploy.yml:
on: push: branches: [main] repository_dispatch: types: [content-updated]
jobs: build: steps: - uses: actions/checkout@v4
- uses: ./.github/actions/content-sync with: token: ${{ secrets.CONTENT_REPO_TOKEN }} repo-url: ${{ secrets.CONTENT_REPO_URL }} content-dir: src/content
- uses: withastro/action@v2 with: package-manager: buntriggers on:
- push to main (site code changes)
repository_dispatch(content changes via webhook)
triggering from content repo
to trigger a deploy when content changes, you need a webhook from the private repo.
option 1: github action in the content repo:
# in lib/.github/workflows/trigger-deploy.ymlon: push: paths: - "log/**" - "wiki/**"
jobs: trigger: runs-on: ubuntu-latest steps: - run: | curl -X POST \ -H "Authorization: token ${{ secrets.SITE_REPO_TOKEN }}" \ -H "Accept: application/vnd.github.v3+json" \ https://api.github.com/repos/thuvasooriya/sooriya/dispatches \ -d '{"event_type":"content-updated"}'option 2: repository webhook (simpler, but less control).
secrets needed
| secret | where | purpose |
|---|---|---|
CONTENT_REPO_TOKEN | sooriya repo | pat to clone private content |
CONTENT_REPO_URL | sooriya repo | full url of content repo |
SITE_REPO_TOKEN | lib repo | pat to trigger site deploy |
local development
for local dev, content is symlinked or copied manually. the action only runs in ci.
you can test the sync locally:
rsync -av --delete \ --exclude='.git' \ --exclude='tmp/' \ ~/arc/lib/log/ ~/arc/dev/sooriya/src/content/gotchas
-
pat expiration: fine-grained pats expire. set a reminder to rotate them.
-
sync is destructive:
--deleteflag means anything not in source gets removed. make sure excludes are right. -
build caching: astro caches content in
.astro/. if things look stale, trybun run clean. -
wiki content: wiki lives in
lib/wiki/notlib/log/wiki/. the rsync paths handle this separately in the actual workflow.