Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 38 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ on:
branches: [main, master]
permissions:
contents: read
# One run per ref: a newer push cancels the older run, so an older build can
# never reach the deploy job after a newer one. The deploy job's own `pages`
# group still serialises deployments.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
test:
Expand Down Expand Up @@ -51,9 +57,18 @@ jobs:
- name: Compile Ddraig
run: idris2 Ddraig.idr -o ddraig
- name: Build sample site
# base-url makes sitemap.xml/feed.xml absolute (valid). Change if the
# site moves to a custom domain.
# base-url makes sitemap.xml/feed.xml absolute (valid), and its path
# (/ddraig-ssg) prefixes the template's root-relative links. Change if
# the site moves to a custom domain.
run: ./build/exec/ddraig build examples _site https://hyperpolymath.github.io/ddraig-ssg
- name: Check sample site
# `ddraig build` exits 0 when it finds no Markdown sources, so the
# .md -> .adoc rename in #17 built an empty site and still passed.
# Fail on that, and on links that would miss the Pages sub-path.
run: |
test -s _site/index.html
test -s _site/about.html
grep -q 'href="/ddraig-ssg/assets/style.css"' _site/index.html
- name: Upload artifact
# v5 pins its inner actions/upload-artifact to a commit SHA; v3 used the
# `@v4` tag, which the SHA-pin policy rejects at job set-up. v4+ drops
Expand All @@ -63,3 +78,24 @@ jobs:
with:
path: _site
include-hidden-files: true
deploy:
# Publish the sample site. Pages is set to build from GitHub Actions, so
# this job is what puts https://hyperpolymath.github.io/ddraig-ssg online.
# Pushes to main only: pull requests build and upload, but never deploy.
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs: build
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
pages: write # create the Pages deployment
id-token: write # OIDC token that deploy-pages exchanges for it
concurrency:
group: pages
cancel-in-progress: false
Comment thread
hyperpolymath marked this conversation as resolved.
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1
53 changes: 44 additions & 9 deletions Ddraig.idr
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,9 @@ buildToc hs =
-- Canonical AAA default template (WCAG 2.2 AAA-capable engine-owned surface):
-- skip link, <html lang>, labelled landmarks, main#main, viewport, stylesheet
-- link. Placeholders: {{title}} {{brand}} {{description}} {{date}} {{toc}}
-- {{content}} — content substituted LAST so its body HTML is never reprocessed.
-- {{root}} {{content}} — content substituted LAST so its body HTML is never
-- reprocessed. {{root}} is the site's path prefix (see `basePath`), so the
-- root-relative stylesheet and home links also work under a sub-path.
defTemplate : String
defTemplate = """
<!DOCTYPE html>
Expand All @@ -556,16 +558,16 @@ defTemplate = """
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="{{title}}">
<meta name="twitter:description" content="{{description}}">
<link rel="stylesheet" href="/assets/style.css">
<link rel="stylesheet" href="{{root}}/assets/style.css">
</head>
<body typeof="schema:WebPage">
<a class="skip-link" href="#main">Skip to content</a>

<header class="site-header" role="banner">
<nav class="container nav" aria-label="Primary" role="navigation" typeof="schema:SiteNavigationElement">
<a class="brand" href="/" property="schema:url"><span property="schema:name">{{brand}}</span></a>
<a class="brand" href="{{root}}/" property="schema:url"><span property="schema:name">{{brand}}</span></a>
<ul class="nav-links">
<li><a href="/" property="schema:url">Home</a></li>
<li><a href="{{root}}/" property="schema:url">Home</a></li>
</ul>
</nav>
</header>
Expand Down Expand Up @@ -748,15 +750,16 @@ stripPlaceholders s = pack (go (unpack s))
else c :: go rest

export
applyTemplate : String -> Frontmatter -> (canonical : String) -> String -> String -> String
applyTemplate template fm canonical htmlContent tocHtml =
applyTemplate : String -> Frontmatter -> (root : String) -> (canonical : String) -> String -> String -> String
applyTemplate template fm root canonical htmlContent tocHtml =
let brand = if fm.brand == "" then fm.title else fm.brand
t1 = strReplace template "{{title}}" (strEscape fm.title)
t2 = strReplace t1 "{{date}}" (strEscape fm.date)
t3 = strReplace t2 "{{description}}" (strEscape fm.description)
t4 = strReplace t3 "{{brand}}" (strEscape brand)
tc = strReplace t4 "{{canonical}}" (strEscape canonical)
t5 = strReplace tc "{{toc}}" tocHtml
tr = strReplace tc "{{root}}" (strEscape root)
t5 = strReplace tr "{{toc}}" tocHtml
-- strip any remaining unfilled placeholders BEFORE injecting content,
-- so author content (which may legitimately contain braces) is untouched
t6 = stripPlaceholders t5
Expand Down Expand Up @@ -917,6 +920,27 @@ absUrl base path =
if base == "" then path
else (if strHasSuffix "/" base then substr 0 (length base `minus` 1) base else base) ++ path

-- The site's path prefix: the path part of the base URL without a trailing
-- slash. "https://example.org/docs/" gives "/docs"; a site at the domain root,
-- or no base URL, gives "". Templates use it as {{root}} for root-relative
-- links, so a project site served under a sub-path (GitHub Pages serves
-- /<repo>/) still finds its stylesheet and home page.
basePath : (base : String) -> String
basePath base = pack (reverse (dropSlashes (reverse (pathOf (afterScheme (unpack base))))))
where
afterScheme : List Char -> List Char
afterScheme (':' :: '/' :: '/' :: rest) = rest
afterScheme (_ :: rest) = afterScheme rest
afterScheme [] = []
-- drop the host, keeping everything from the first '/'
pathOf : List Char -> List Char
pathOf [] = []
pathOf cs@('/' :: _) = cs
pathOf (_ :: rest) = pathOf rest
dropSlashes : List Char -> List Char
dropSlashes ('/' :: rest) = dropSlashes rest
dropSlashes cs = cs

-- Build one markdown file. Returns Maybe Page (Nothing if draft skipped).
-- Build one markdown file. Returns the Page plus its O3 certification status
-- (certified?, violations). Nothing only when the file is a draft or unreadable.
Expand All @@ -938,7 +962,7 @@ buildOne src out base rel = do
let fm2 = { title := title } fm
let relHtml = toHtmlPath rel
let canonical = absUrl base ("/" ++ relHtml)
let html = applyTemplate tpl fm2 canonical md.body toc
let html = applyTemplate tpl fm2 (basePath base) canonical md.body toc
let outPath = joinPath out relHtml
_ <- mkDirP (dirOf outPath)
w <- writeFile outPath html
Expand Down Expand Up @@ -1224,8 +1248,19 @@ testFull = do
let content = "---\ntitle: Welcome\ndate: 2024-01-15\n---\n\n# Welcome\n\nThis is **Ddraig**, an Idris 2-powered SSG.\n\n- Dependently typed\n- Provably correct\n"
let (fm, body) = parseFrontmatter content
let md = parseMarkdownFull body
let output = applyTemplate defTemplate fm "/welcome.html" md.body (buildToc md.toc)
let output = applyTemplate defTemplate fm "" "/welcome.html" md.body (buildToc md.toc)
putStrLn output
-- {{root}}: a sub-path site's links must carry its prefix.
let cases = [ ("", ""), ("https://example.com", ""), ("https://example.com/", "")
, ("https://hyperpolymath.github.io/ddraig-ssg", "/ddraig-ssg")
, ("https://example.org/a/b/", "/a/b") ]
let wrong = filter (\(b, want) => basePath b /= want) cases
let rooted = applyTemplate defTemplate fm "/sub" "/sub/welcome.html" md.body ""
let styled = strReplace rooted "href=\"/sub/assets/style.css\"" "" /= rooted
if null wrong && styled
then putStrLn "OK: basePath and {{root}}"
else do putStrLn ("FAIL: basePath/{{root}} " ++ show wrong ++ " stylesheet-prefixed=" ++ show styled)
exitFailure

-- ============================================================================
-- a2ml STATE descriptile validation (O1)
Expand Down
7 changes: 7 additions & 0 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ toc::[]
markup.
* *Templating* — file-based layouts (`templates/<layout>.html`), partials
(`{{> name}}` from `templates/partials/`), a `{{toc}}` table of contents,
a `{{root}}` path prefix for root-relative links (see `base-url` below),
and a built-in accessible fallback template.
* *Assets* — copies `assets/`, `static/`, `css/`, `js/`, `images/`, and
`public/` (the latter to the site root, so `/.well-known/security.txt`,
Expand Down Expand Up @@ -102,6 +103,12 @@ absolute `<loc>` URLs to be valid, and the feed embeds absolute `<id>`/`<link>`
values; without a base URL the feed still validates (it falls back to `urn:`
ids) but the sitemap locations are relative.

If the site is served under a sub-path, include it in `base-url` (for a GitHub
Pages project site, `https://<user>.github.io/<repo>`). Its path becomes
`{{root}}` (here `/<repo>`), which the default template puts in front of its
stylesheet and home links. Write links between your own pages as relative
paths (`about.html`), not root-relative ones (`/about.html`).

A minimal example site lives in `examples/`:

[source,bash]
Expand Down
2 changes: 1 addition & 1 deletion examples/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ main : IO ()
main = putStrLn "Ddraig"
```

See the [getting-started page](/about.html) and the project [README](https://github.com/hyperpolymath/ddraig-ssg).
See the [getting-started page](about.html) and the project [README](https://github.com/hyperpolymath/ddraig-ssg).
Loading