diff --git a/.github/workflows/deploy-cloudflare.yml b/.github/workflows/deploy-cloudflare.yml
new file mode 100644
index 0000000..b18ff41
--- /dev/null
+++ b/.github/workflows/deploy-cloudflare.yml
@@ -0,0 +1,45 @@
+name: Deploy Cloudflare Pages
+
+on:
+ push:
+ branches:
+ - main
+ workflow_dispatch:
+
+permissions:
+ contents: read
+
+concurrency:
+ group: cloudflare-pages-production
+ cancel-in-progress: false
+
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup Node
+ uses: actions/setup-node@v4
+ with:
+ node-version: 22
+ cache: npm
+
+ - name: Setup Hugo
+ uses: peaceiris/actions-hugo@v3
+ with:
+ hugo-version: latest
+ extended: true
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Build site
+ run: npm run build
+
+ - name: Deploy to Cloudflare Pages
+ env:
+ CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
+ CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
+ run: npx wrangler pages deploy public --project-name open-rtls-website --branch main
diff --git a/.github/workflows/pr-validate.yml b/.github/workflows/pr-validate.yml
new file mode 100644
index 0000000..54d0ceb
--- /dev/null
+++ b/.github/workflows/pr-validate.yml
@@ -0,0 +1,34 @@
+name: PR Validate Hugo
+
+on:
+ pull_request:
+ branches:
+ - main
+
+permissions:
+ contents: read
+
+jobs:
+ validate:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup Node
+ uses: actions/setup-node@v4
+ with:
+ node-version: 22
+ cache: npm
+
+ - name: Setup Hugo
+ uses: peaceiris/actions-hugo@v3
+ with:
+ hugo-version: latest
+ extended: true
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Build site
+ run: npm run build
diff --git a/README.md b/README.md
index 50e3e08..8855377 100644
--- a/README.md
+++ b/README.md
@@ -34,7 +34,53 @@ Use these settings:
`wrangler.toml` is included as a Pages-ready scaffold for future iteration needs.
-### Direct deploy from this machine
+## GitHub Publishing Flow
+
+Publishing is handled by GitHub Actions:
+
+- Pull requests to `main` run validation (`.github/workflows/pr-validate.yml`)
+- Pushes/merges to `main` auto-deploy to Cloudflare Pages (`.github/workflows/deploy-cloudflare.yml`)
+
+### 1. Create API token permissions in Cloudflare
+
+- `Account` -> `Cloudflare Pages` -> `Edit`
+- `Zone` -> `DNS` -> `Edit`
+- `Zone` -> `Zone` -> `Read`
+- Zone Resources: include `open-rtls.com`
+
+### 2. Set GitHub Actions secrets
+
+Create repository secrets in GitHub:
+
+- `CLOUDFLARE_API_TOKEN`
+- `CLOUDFLARE_ACCOUNT_ID`
+
+You can load these from `cloudflare.env` using GitHub CLI:
+
+```bash
+while IFS='=' read -r key value; do
+ [[ -z "$key" || "$key" =~ ^# ]] && continue
+ gh secret set "$key" --body "$value"
+done < cloudflare.env
+```
+
+### 3. One-time domain setup in Cloudflare Pages
+
+- Add custom domains to the Pages project:
+ - `open-rtls.com`
+ - `www.open-rtls.com`
+- In DNS, point both hostnames to `open-rtls-website.pages.dev` as `CNAME` records.
+- Keep DNS records as `DNS only` until domain status is `active`.
+- Add redirect rule:
+ - Source: `https://www.open-rtls.com/*`
+ - Target: `https://open-rtls.com/$1`
+ - Status: `301`
+
+After that, merging to `main` publishes automatically.
+
+### Optional: direct local deploy
+
+If you need to deploy from a local machine instead of GitHub Actions:
1. Create API token permissions in Cloudflare:
- `Account` -> `Cloudflare Pages` -> `Edit`
@@ -72,4 +118,4 @@ This command builds the site, ensures the Pages project exists, and deploys to p
## Scope
This first iteration focuses on a usable initial website version.
-Deployment automation and additional operational tooling will be added in subsequent iterations.
+Deployment automation is configured through GitHub Actions.
diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html
index 839fab8..417a5ea 100644
--- a/layouts/_default/baseof.html
+++ b/layouts/_default/baseof.html
@@ -15,6 +15,7 @@
+
{{ $styles := resources.Get "css/main.css" | css.PostCSS }}
{{ if not hugo.IsServer }}