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
58 changes: 58 additions & 0 deletions .github/workflows/pages.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Pages

on:
push:
branches:
- main
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: pages
cancel-in-progress: false

defaults:
run:
shell: bash

jobs:
build:
runs-on: ubuntu-latest
env:
HUGO_VERSION: 0.163.3
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Configure Pages
uses: actions/configure-pages@v6

- name: Install Hugo
run: |
mkdir -p "${HOME}/.local/hugo"
curl -sfL --output-dir "${{ runner.temp }}" -O "https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_linux-amd64.tar.gz"
tar -C "${HOME}/.local/hugo" -xf "${{ runner.temp }}/hugo_${HUGO_VERSION}_linux-amd64.tar.gz"
echo "${HOME}/.local/hugo" >> "${GITHUB_PATH}"

- name: Build
run: hugo --cleanDestinationDir --minify --cacheDir "${{ runner.temp }}/hugo_cache"

- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v5
with:
path: ./public

deploy:
runs-on: ubuntu-latest
needs: build
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v5
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ acceptance/vendor

# Go build and release artifacts
/dist/
/public/
/.hugo_build.lock

# Go binary build artifact in cmd
cmd/facts/facts
Expand Down
12 changes: 12 additions & 0 deletions docs/SITE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Documentation Site

Facts publishes a Hugo documentation site at `https://facts.martinez.io/`.

Build it from the repository root:

```sh
hugo --cleanDestinationDir --minify
```

The site uses `docs/` as Hugo content, writes generated HTML to `public/`, and
publishes that directory through GitHub Pages. Do not commit `public/`.
9 changes: 9 additions & 0 deletions docs/adr/0013-facts-docs-dedicated-subdomain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Facts docs use a dedicated custom subdomain

Facts will publish its documentation site at `https://facts.martinez.io/` from this repository, rather than under the existing `martinez.io` personal site or the default `ncode.github.io/facts` repository Pages URL. The site should stay independently deployable from the Facts repo, with GitHub Pages serving the generated site and a `CNAME` for `facts.martinez.io`.

## Considered Options

- **Use `facts.martinez.io`** - chosen: it gives Facts a stable public URL while keeping the project docs independent from the existing personal site.
- **Use `martinez.io/facts`** - rejected: it would couple Facts documentation deployment and routing to the personal website.
- **Use `ncode.github.io/facts`** - rejected: it works as a fallback, but the custom domain is cleaner for public documentation.
14 changes: 14 additions & 0 deletions hugo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
baseURL = "https://facts.martinez.io/"
title = "Facts"
contentDir = "docs"
publishDir = "public"
disableKinds = ["rss", "taxonomy", "term"]

[markup]
[markup.goldmark]
[markup.goldmark.renderer]
unsafe = false

[caches]
[caches.images]
dir = ":cacheDir/images"
29 changes: 29 additions & 0 deletions layouts/_default/_markup/render-link.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{{- $href := .Destination -}}
{{- if not (or (hasPrefix $href "http://") (hasPrefix $href "https://") (hasPrefix $href "#") (hasPrefix $href "mailto:")) -}}
{{- $clean := replaceRE `^(\./|\.\./)+` "" $href -}}
{{- if eq $clean "adr/" -}}
{{- $href = "/adr/" -}}
{{- else if or (eq $clean "schema/facts.yaml") (eq $clean "docs/schema/facts.yaml") -}}
{{- $href = "https://github.com/ncode/facts/blob/main/docs/schema/facts.yaml" -}}
{{- else if or (eq $clean "supported-facts/") (eq $clean "docs/supported-facts/") -}}
{{- $href = "/supported-facts/" -}}
{{- else if hasSuffix $clean ".md" -}}
{{- $stem := replaceRE `\.md$` "" (path.Base $clean) | lower -}}
{{- if hasPrefix $clean "adr/" -}}
{{- $href = printf "/adr/%s/" $stem -}}
{{- else if eq $stem "history" -}}
{{- $href = "/history/" -}}
{{- else if eq $stem "custom_fact_migration" -}}
{{- $href = "/custom_fact_migration/" -}}
{{- else if eq $stem "facter_conf_compatibility" -}}
{{- $href = "/facter_conf_compatibility/" -}}
{{- else if and (or (eq .Page.Section "supported-facts") (hasPrefix $clean "supported-facts/") (hasPrefix $clean "docs/supported-facts/")) (ne $stem "readme") -}}
{{- $href = printf "/supported-facts/%s/" $stem -}}
{{- else -}}
{{- $href = printf "https://github.com/ncode/facts/blob/main/%s" $clean -}}
{{- end -}}
{{- else if hasPrefix $clean "docs/" -}}
{{- $href = printf "https://github.com/ncode/facts/blob/main/%s" $clean -}}
{{- end -}}
{{- end -}}
<a href="{{ $href }}"{{ with .Title }} title="{{ . }}"{{ end }}>{{ .Text | safeHTML }}</a>
31 changes: 31 additions & 0 deletions layouts/_default/baseof.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ if .IsHome }}{{ site.Title }}{{ else }}{{ .Title }} | {{ site.Title }}{{ end }}</title>
<meta name="description" content="Facts is a Go port of Puppet Facter: an embeddable library and the facts CLI.">
<link rel="stylesheet" href="{{ "css/site.css" | relURL }}">
</head>
<body>
<header class="site-header">
<div class="site-header-inner">
<a class="site-wordmark" href="{{ "/" | relURL }}">Facts</a>
<nav class="site-nav" aria-label="Primary">
<a href="{{ "/" | relURL }}">Start</a>
<a href="{{ "/#library" | relURL }}">Library</a>
<a href="{{ "/#cli" | relURL }}">CLI</a>
<a href="{{ "/supported-facts/" | relURL }}">Supported facts</a>
<a href="{{ "/history/" | relURL }}">Project</a>
</nav>
</div>
</header>
{{ block "main" . }}{{ end }}
<footer class="site-footer">
<div class="site-footer-inner">
<span>Apache-2.0 · Facts</span>
<span><a href="https://github.com/ncode/facts">GitHub</a> · <a href="https://pkg.go.dev/github.com/ncode/facts">Go package</a></span>
</div>
</footer>
</body>
</html>
25 changes: 25 additions & 0 deletions layouts/_default/list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{{ define "main" }}
<main class="doc-shell">
{{ partial "docs-nav.html" . }}
<article class="doc-content">
{{ if eq .Section "supported-facts" }}
{{ with site.GetPage "/supported-facts/readme" }}
{{ .Content }}
{{ else }}
<h1>{{ partial "page-title.html" . }}</h1>
{{ end }}
{{ else if .Content }}
{{ .Content }}
{{ else }}
<h1>{{ partial "page-title.html" . }}</h1>
{{ end }}
<div class="doc-list">
{{ range sort .RegularPages "File.Path" }}
{{ if ne (.File.TranslationBaseName | lower) "readme" }}
<a href="{{ .RelPermalink }}">{{ partial "page-title.html" . }}</a>
{{ end }}
{{ end }}
</div>
</article>
</main>
{{ end }}
8 changes: 8 additions & 0 deletions layouts/_default/single.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{{ define "main" }}
<main class="doc-shell">
{{ partial "docs-nav.html" . }}
<article class="doc-content">
{{ .Content }}
</article>
</main>
{{ end }}
79 changes: 79 additions & 0 deletions layouts/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
{{ define "main" }}
<main>
<section class="hero">
<div class="band-inner hero-content">
<div class="eyebrow">Facts — a Go port of Puppet Facter</div>
<h1>Every fact about the machine.</h1>
<p class="hero-lede">Facts discovers hardware, networking, OS, cloud metadata, and operator-supplied facts as one canonical tree: embeddable as a Go library, scriptable as the <code>facts</code> CLI.</p>
<div class="hero-actions">
<a class="button button-primary" href="https://pkg.go.dev/github.com/ncode/facts">Go library</a>
<a class="button" href="{{ "/facter_conf_compatibility/" | relURL }}">CLI compatibility</a>
<a class="button" href="{{ "/supported-facts/linux/" | relURL }}">Supported facts</a>
</div>
<div class="command-strip" aria-label="Install commands">
<pre><code>$ go get github.com/ncode/facts
$ brew install ncode/tap/facts
$ facts --json os.family kernel.version.full</code></pre>
</div>
</div>
</section>

<section class="band" id="library">
<div class="band-inner">
<div class="section-heading">
<div class="eyebrow">Library</div>
<h2>Hermetic by default.</h2>
<p>An engine is an isolated, immutable unit of discovery configuration. It reads core facts only until you opt into config files, external facts, registered facts, or system defaults.</p>
</div>
<div class="feature-grid">
<article class="feature-card">
<h3>Engine</h3>
<p>Construct explicit discovery inputs with no package-global collector and no shared mutable state.</p>
</article>
<article class="feature-card">
<h3>Snapshot</h3>
<p>Discover once, then query and decode an immutable canonical tree as often as needed.</p>
</article>
<article class="feature-card">
<h3>Typed views</h3>
<p>Decode subtrees into caller-owned Go types and fail loudly when the shape does not match.</p>
</article>
</div>
</div>
</section>

<section class="band band-soft" id="cli">
<div class="band-inner">
<div class="section-heading">
<div class="eyebrow">CLI</div>
<h2>Every fact, one command.</h2>
<p>The <code>facts</code> binary keeps the Ruby Facter process-boundary contract for output formatting, exit status, stderr diagnostics, external facts, environment facts, and config semantics.</p>
</div>
<pre><code>$ facts os.name
Darwin

$ facts --external-dir ./facts.d site_role
web</code></pre>
</div>
</section>

<section class="band" id="supported-facts">
<div class="band-inner">
<div class="section-heading">
<div class="eyebrow">Supported facts</div>
<h2>Tested where it ships.</h2>
<p>The schema-backed supported fact pages are generated from <code>docs/schema/facts.yaml</code> and rendered here without becoming a second source of truth.</p>
</div>
<div class="platform-grid">
{{ with site.GetPage "/supported-facts" }}
{{ range sort .RegularPages "File.Path" }}
{{ if ne (.File.TranslationBaseName | lower) "readme" }}
<a class="platform-card" href="{{ .RelPermalink }}"><h3>{{ partial "page-title.html" . }}</h3><span class="platform-count">Schema-backed reference</span></a>
{{ end }}
{{ end }}
{{ end }}
</div>
</div>
</section>
</main>
{{ end }}
33 changes: 33 additions & 0 deletions layouts/partials/docs-nav.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<aside class="doc-nav" aria-label="Documentation">
<div class="doc-nav-group">
<div class="doc-nav-title">Start</div>
<a href="{{ "/" | relURL }}">Overview</a>
<a href="{{ "/site/" | relURL }}">Site build notes</a>
</div>
<div class="doc-nav-group">
<div class="doc-nav-title">Library</div>
<a href="https://pkg.go.dev/github.com/ncode/facts">Go API reference</a>
<a href="{{ "/custom_fact_migration/" | relURL }}">External facts</a>
</div>
<div class="doc-nav-group">
<div class="doc-nav-title">CLI</div>
<a href="{{ "/facter_conf_compatibility/" | relURL }}">Input compatibility</a>
<a href="{{ "/custom_fact_migration/" | relURL }}">Custom fact migration</a>
</div>
<div class="doc-nav-group">
<div class="doc-nav-title">Supported facts</div>
<a href="{{ "/supported-facts/" | relURL }}">Overview</a>
{{ with site.GetPage "/supported-facts" }}
{{ range sort .RegularPages "File.Path" }}
{{ if ne (.File.TranslationBaseName | lower) "readme" }}
<a href="{{ .RelPermalink }}">{{ partial "page-title.html" . }}</a>
{{ end }}
{{ end }}
{{ end }}
</div>
<div class="doc-nav-group">
<div class="doc-nav-title">Project</div>
<a href="{{ "/history/" | relURL }}">History</a>
<a href="{{ "/adr/" | relURL }}">ADRs</a>
</div>
</aside>
10 changes: 10 additions & 0 deletions layouts/partials/page-title.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{{- $title := "" -}}
{{- with findRE `(?m)^#\s+(.+)$` .RawContent 1 -}}
{{- $title = replaceRE `^#\s+` "" (index . 0) -}}
{{- else -}}
{{- $title = .Title -}}
{{- if and (not $title) .File -}}
{{- $title = .File.TranslationBaseName | humanize | title -}}
{{- end -}}
{{- end -}}
{{- return $title -}}
2 changes: 2 additions & 0 deletions openspec/changes/add-hugo-github-pages-site/.openspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-06-25
38 changes: 38 additions & 0 deletions openspec/changes/add-hugo-github-pages-site/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
## Context

Facts already has a polished README, generated supported-fact Markdown, and reference docs under `docs/`, but it does not publish a standalone website. The agreed direction is a Hugo site published from this repository to `https://facts.martinez.io/`, independent from the existing `martinez.io` personal site.

The current repository has no JavaScript site pipeline. The site should therefore add only Hugo source and a GitHub Pages workflow, keeping generated HTML out of git and keeping `docs/schema/facts.yaml` plus `tools/supportedfacts` as the owners of supported-fact reference content.

## Goals / Non-Goals

**Goals:**

- Publish a static Hugo documentation site for Facts at `https://facts.martinez.io/`.
- Reuse existing Markdown documentation under `docs/` instead of duplicating docs into a second tree.
- Build and deploy with GitHub Actions using the Pages artifact flow.
- Match the README visual language and the Vercel-inspired reference: near-white surfaces, near-black ink, mono technical labels, restrained cards, and a hero-scale mesh gradient.
- Keep v1 dependency-light: no npm, no theme dependency, no client-side JavaScript requirement.

**Non-Goals:**

- No docs search, version switcher, analytics, or interactive terminal in v1.
- No committed generated HTML.
- No change to fact schema generation, fact resolution, CLI output, or library API.
- No merge with the existing `martinez.io` website.

## Decisions

- **Use Hugo with custom layouts.** Hugo renders the existing Markdown and gives GitHub Pages a plain static artifact. A third-party theme is rejected because it would add dependency churn and fight the README-derived design.
- **Keep Hugo source at the repository root and `docs/` as content.** Root-level `hugo.toml`, `layouts/`, and `static/` keep the site obvious while avoiding a duplicate `site/content` copy of the docs.
- **Use `facts.martinez.io` as the canonical base URL.** This follows ADR-0013 and keeps Facts documentation independent from the personal site. `static/CNAME` owns the Pages custom-domain file.
- **Publish from GitHub Actions artifacts.** The workflow builds Hugo into `public/` and deploys with the GitHub Pages artifact actions. Generated HTML stays out of git.
- **Let the schema generator own supported-fact pages.** Hugo only renders `docs/supported-facts/*.md`; the Go generator and existing tests remain responsible for keeping them in sync with `docs/schema/facts.yaml`.
- **Use system fonts and local CSS only.** The README SVG already uses platform sans and mono stacks. External font packages, npm, and client-side scripts are rejected for v1.

## Risks / Trade-offs

- **Markdown without front matter may render with weak titles** -> derive titles in Hugo templates from headings or file names, and add front matter only to hand-authored docs if needed.
- **`docs/` contains non-page assets and generated files** -> configure Hugo/layouts so only intended Markdown pages appear in navigation, while static assets are served from `static/` or copied deliberately.
- **GitHub Pages custom domain needs DNS outside the repo** -> commit `static/CNAME` and document that DNS for `facts.martinez.io` must point at the Pages host.
- **No theme means fewer built-in docs features** -> acceptable for v1; add search or deeper navigation only when docs volume requires it.
29 changes: 29 additions & 0 deletions openspec/changes/add-hugo-github-pages-site/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
## Why

Facts has a strong README and generated reference docs, but no standalone public documentation site. A GitHub Pages site gives Facts a stable project URL at `https://facts.martinez.io/` while keeping the existing Markdown docs as the source of truth.

## What Changes

- Add a Hugo-powered documentation site for Facts.
- Publish the site with GitHub Pages from a GitHub Actions build artifact, not committed generated HTML.
- Use the existing `docs/` Markdown and generated supported-fact pages as Hugo content.
- Add a custom domain CNAME for `facts.martinez.io`.
- Style the site from the README visual language: near-white canvas, near-black ink, mono technical labels, and the existing mesh-gradient hero palette.
- Keep v1 static: no npm, no Hugo modules, no theme dependency, no client-side interactivity.

## Capabilities

### New Capabilities

- `facts-documentation-site`: Public Hugo documentation site for Facts, including homepage, docs navigation, custom domain, and GitHub Pages deployment.

### Modified Capabilities

(none)

## Impact

- **Docs/site**: root Hugo configuration, custom layouts, CSS, static assets, CNAME, and docs content metadata or index pages as needed.
- **CI/deployment**: GitHub Actions workflow for building Hugo and publishing to GitHub Pages.
- **Existing docs**: `docs/supported-facts/*.md` remain generated from `docs/schema/facts.yaml`; Hugo renders them but does not own their content.
- **Product behavior**: No change to fact resolution, CLI behavior, library API, schema contract, or release target support.
Loading
Loading