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
10 changes: 7 additions & 3 deletions .github/workflows/vale-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,10 @@ jobs:
INLINE: ${{ inputs.inline_annotations }}
CHANGED: ${{ steps.changed-files.outputs.all_changed_files }}
run: |
# `shell: bash` runs with -e and -o pipefail. Capture Vale's exit code
# rather than letting -e abort on 1, which would skip the annotation
# output and the advisory-or-blocking decision below.
set +e
set -u
# Not through xargs: it reports 123 for any non-zero exit of the
# command it runs, which would hide Vale's 1-versus-2 distinction.
Expand All @@ -217,13 +221,13 @@ jobs:
echo "code=$code" >> "$GITHUB_OUTPUT"
case "$code" in
0) exit 0 ;;
1) [ "$FAIL" = "true" ] && exit 1 || exit 0 ;;
1) if [ "$FAIL" = "true" ]; then exit 1; else exit 0; fi ;;
*) echo "::error::Vale exited $code before checking every file. See the log."; exit "$code" ;;
esac

# Every finding, not only the ten per level that GitHub shows inline.
- name: Summarise findings
if: ${{ always() && steps.vale.outputs.code != '' }}
if: ${{ always() && steps.changed-files.outputs.any_changed == 'true' }}
shell: bash
env:
MODE: ${{ steps.mode.outputs.mode }}
Expand All @@ -249,7 +253,7 @@ jobs:
echo
echo "| Level | File | Line | Rule | Message |"
echo "|---|---|---|---|---|"
sed -E 's/^::([a-z]+) file=([^,]+),line=([0-9]+),col=[0-9]+,endColumn=[0-9]+,title=([^:]+)::(.*)$/| \1 | `\2` | \3 | `\4` | \5 |/' \
sed -E 's/^::([a-z]+) file=([^,]+),line=([0-9]+),col=[0-9]+,endColumn=[0-9]+,title=([^:]+)::(.*)$/| \1 | \2 | \3 | \4 | \5 |/' \
vale-annotations.txt
fi
} >> "$GITHUB_STEP_SUMMARY"
11 changes: 6 additions & 5 deletions .vale.ini
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,12 @@ mdx = md

[*.{md,mdx}]

# ignore includes, inline code, paths, and latex math code
# Ignore includes, MDX syntax, paths, and latex math code. Not inline code,
# fenced blocks or Markdown links: Vale's Markdown parser already skips code
# and link targets and lints link text as prose. A regex for any of them on
# top desynchronises that parsing, which makes Vale lint inside fences, inline
# code and link targets and report prose on the wrong line.
TokenIgnores = ({![^!}]+!}),\
(`[^`]+`),\
(\$[^\n$`]+\$),\
(\$\$[^$`]+\$\$),\
(\$\{\{[^}]+\}\}),\
Expand All @@ -49,14 +52,12 @@ TokenIgnores = ({![^!}]+!}),\
(\bPolyfill\b),\
((?i)TL;DR),\
(\[!(?:NOTE|TIP|IMPORTANT|WARNING|CAUTION)\]),\
(\[[^\]]+\]\([^)]+\)),\
(\b(?:backend|examples|docs|packages|workers|containers|power-meter|temperature|miners|pools|plugins|deploy|node_modules)(?:/[\w.-]+)+\b),\
([Pp]ower meters)

# Ignore MDX import/export lines, fenced code blocks, and reference-link definitions.
# Ignore MDX import/export lines and reference-link definitions.
BlockIgnores = (?m)^\s*import\s+[^\n]*$,\
(?m)^\s*export\s+[^\n]*$,\
(?s)```[^\n]*\n.*?```,\
(?m)^\[[^\]]+\]:\s+.+$

# List of styles to load
Expand Down
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
- Source code and content of the docs website
- Automation scripts for the integration between the codebase and the documentation

The site is a **static export** from a Next.js + [Fumadocs](https://fumadocs.dev) app (`output: 'export'`). SEO behavior is implemented with workspace packages under `@tether/docs-*` (see below).

Check warning on line 8 in README.md

View workflow job for this annotation

GitHub Actions / vale

Tether.Acronyms

'SEO' has no definition.

## Table of contents

- [Installation](#installation)
- [Monorepo packages (`packages/`)](#monorepo-packages-packages)
- [Using these packages from another repository](#using-these-packages-from-another-repository)
- [SEO and frontmatter](#seo-and-frontmatter)

Check warning on line 15 in README.md

View workflow job for this annotation

GitHub Actions / vale

Tether.Acronyms

'SEO' has no definition.
- [Environment variables](#environment-variables)
- [Open Graph images (Takumi, static hosting)](#open-graph-images-takumi-static-hosting)
- [Development](#development)
Expand Down Expand Up @@ -44,7 +44,7 @@
cp env.example .env.local
```

See [`env.example`](env.example) for all variables. **Required** for production SEO:

Check warning on line 47 in README.md

View workflow job for this annotation

GitHub Actions / vale

Tether.Acronyms

'SEO' has no definition.

- **`NEXT_PUBLIC_DOCS_ORIGIN`** — public docs URL for canonical and social metadata ([`seo-config.ts`](src/lib/seo-config.ts)); optional for local dev (defaults to `http://localhost:3001`)

Expand All @@ -59,7 +59,7 @@
|--------|------|
| `@tether/docs-seo-schema` | Zod `tetherSeoFrontmatterSchema`: **required** `description`; optional `noIndex`, `ogImage`, `schemaType`, `docType`, `lastModified`. Exports `warnMissingSeoFrontmatterFields`, `DOCS_SEO_WARN_PREFIX`. |
| `@tether/docs-seo-core` | `getPageSeoState` (canonical, `ogImage` override, `PageSeoState` with `slugs` / `seoAuditFields`), `buildJsonLdGraph`, `inferJsonLdType`. Root-relative **`ogImage`** paths are turned into absolute URLs **without** applying doc `trailingSlash` (so `/asset.png` does not become `/asset.png/`). |
| `@tether/docs-seo-next` | `buildDocsMetadata`, `buildDocsSitemap`, `buildDocsRobots`, `DocsJsonLd`; re-exports core/schema SEO helpers. |

Check warning on line 62 in README.md

View workflow job for this annotation

GitHub Actions / vale

Tether.Acronyms

'SEO' has no definition.
| `@tether/docs-seo-og` | `getPageImage` (URL layout), Takumi **prebuild** (`@tether/docs-seo-og/build`), optional Route Handler (`@tether/docs-seo-og/handler`); re-exports `warnMissingSeoFrontmatterFields`. |

The main app must **not** import `@tether/docs-seo-og/handler` in pages that ship to the static bundle; Takumi is only used at **prebuild** time or in a server Route Handler.
Expand All @@ -81,14 +81,14 @@

#### 2. Generate a personal access token

Create a **classic** PAT at https://github.com/settings/tokens/new with these scopes:

Check warning on line 84 in README.md

View workflow job for this annotation

GitHub Actions / vale

Tether.Acronyms

'PAT' has no definition.

- **`read:packages`** — required, to download `@tetherto/*` from GitHub Packages
- **`repo`** — required if any of the source repositories that publish those packages are private (otherwise GitHub returns `403` even with the right scope)

Important: **set an expiration on the token.** Tokens that never expire are rejected by the registry. Pick the shortest expiration that fits your workflow; you'll be regenerating it.

After creating the token, if the `tetherto` organization uses SSO, click **"Configure SSO"** next to the token in the tokens list and authorize it for the org. Without that step the registry returns `403` even with the right scopes.

Check warning on line 91 in README.md

View workflow job for this annotation

GitHub Actions / vale

Tether.Acronyms

'SSO' has no definition.

Check warning on line 91 in README.md

View workflow job for this annotation

GitHub Actions / vale

Tether.Acronyms

'SSO' has no definition.

A fine-grained personal access token also works, scoped to the `tetherto` resource owner with **Packages read** repository permission. Same expiration requirement applies.

Expand All @@ -106,7 +106,7 @@
npm view @tetherto/docs-seo-schema version --registry=https://npm.pkg.github.com
```

If `npm whoami` prints your username but the second command returns HTTP 403, the token authenticates but lacks `read:packages` (and/or SSO authorization).

Check warning on line 109 in README.md

View workflow job for this annotation

GitHub Actions / vale

Tether.Acronyms

'SSO' has no definition.

#### 5. Install

Expand All @@ -115,7 +115,7 @@
npm install
```

CI sets `GITHUB_TOKEN` directly from secrets (e.g. the built-in `secrets.GITHUB_TOKEN` for org-internal workflows, or a PAT secret for cross-org reads) — no `.env` file involved.

Check warning on line 118 in README.md

View workflow job for this annotation

GitHub Actions / vale

Tether.Acronyms

'PAT' has no definition.

#### Notes for the consumer's `package.json`

Expand All @@ -123,7 +123,7 @@
- Sources are TypeScript and shipped as-is, so set `transpilePackages` in **Next.js** to include every `@tetherto/docs-seo-*` package you import.
- Install **peer dependencies** (`fumadocs-core`, `next`, `react`, `zod` for schema, etc.) to versions compatible with each package's `peerDependencies`.

## SEO and frontmatter

Check warning on line 126 in README.md

View workflow job for this annotation

GitHub Actions / vale

Tether.Acronyms

'SEO' has no definition.

Extended fields are merged in [`source.config.ts`](source.config.ts) via `tetherSeoFrontmatterSchema`. **`description`** is **required** (non-empty after trim) on every docs page for meta tags, Open Graph / Twitter, and JSON-LD. Other fields are optional:

Expand Down Expand Up @@ -191,7 +191,7 @@
brew install vale
```

Use the project vocabulary to check custom spelling. For example, Tether should pass, while Tehtr should fail.

Check failure on line 194 in README.md

View workflow job for this annotation

GitHub Actions / vale

Vale.Spelling

Did you really mean 'Tehtr'?

Run Vale against a single file:

Expand Down Expand Up @@ -241,7 +241,26 @@
This repository provides an opinionated Vale configuration. To use in downstream documentation repositories either:

- **Lint against the shared config** — call this repository's reusable workflow with `central_config: true`. It checks out this template beside the downstream repository and lints against the central [`.vale.ini`](.vale.ini) and [`styles/`](styles/), advisory by default (`fail_on_error: false`).
- **Lint against your own config, on the shared rules** — call the reusable workflow with `config_path` pointing at a `.vale.ini` in the downstream repository. The template is still checked out beside it at `.tether-vale-source`, so that file can set `StylesPath = .tether-vale-source/styles` and choose which Tether rules apply and at what level, without copying any rule. Use this when a repository needs a different alert floor from the central one.
- **Lint against your own config, on the shared rules** — call the reusable workflow with `config_path` pointing at a `.vale.ini` in the downstream repository. The template is still checked out beside it at `.tether-vale-source`, so that file can set `StylesPath = .tether-vale-source/styles` and choose which Tether rules apply and at what level, without copying any rule. Use this when a repository needs a different alert floor from the central one. A minimal override file:

```ini
# Rules come from docs-template, checked out beside this repo by the shared workflow.
StylesPath = .tether-vale-source/styles
Vocab = Tether-common

# Alert floor: only rules at or above this level surface.
MinAlertLevel = error

[*.md]
# Which shared styles to load. Omit Google, proselint and write-good to skip
# their warnings entirely.
BasedOnStyles = Tether, Vale

# Per-rule levels, raised or lowered as this repository needs.
Tether.CaseSensitive-Substitution = error
Tether.Headings = error
```

- **Lint against a fully custom config** — keep a local `.vale.ini` and styles in the downstream repository and run Vale in that repository's own CI (the [Vale linting](#vale-linting) setup above covers the CLI).

The reusable workflow's self-lint mode (`central_config: false` and no `config_path`) is reserved for this template: it uses the local `.vale.ini` and always fails the job on errors, so it isn't a configuration path for downstream repositories.
Expand Down
Loading