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
27 changes: 27 additions & 0 deletions .github/workflows/wiki-sync.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Sync Wiki

on:
push:
branches: [main]
paths:
- 'docs/**'
- 'scripts/sync-wiki.ps1'
- '.github/workflows/wiki-sync.yml'
workflow_dispatch:

jobs:
sync:
runs-on: windows-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4

- name: Sync docs/ to GitHub wiki
shell: pwsh
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
$repo = '${{ github.repository }}'
$wikiUrl = "https://x-access-token:$env:GH_TOKEN@github.com/$repo.wiki.git"
./scripts/sync-wiki.ps1 -WikiUrl $wikiUrl
147 changes: 147 additions & 0 deletions scripts/sync-wiki.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
<#
.SYNOPSIS
Mirrors the in-repo docs/ folder to a GitHub or Gitea wiki repo.

.DESCRIPTION
Wikis are separate git repositories (e.g. <repo>.wiki.git) with a flat URL
structure. This script:

1. Clones the wiki repo into a temp directory.
2. Wipes its existing .md content.
3. Copies each docs/*.md to a flattened wiki-style page name.
4. Rewrites in-repo markdown links so they point at the wiki page slugs.
5. Generates a _Sidebar.md so every wiki page has a navigation sidebar.
6. Commits and pushes back if anything changed.

Idempotent. Safe to re-run.

.PARAMETER WikiUrl
Full HTTPS URL to the wiki repo, including any embedded credentials. Examples:
https://github.com/recklessop/webhook-server.wiki.git
https://x-access-token:$TOKEN@github.com/recklessop/webhook-server.wiki.git
https://justin:$GITEA_TOKEN@git.jpaul.io/justin/webhook-server.wiki.git

.PARAMETER AuthorName
git committer name. Defaults to "Webhook Server Wiki Sync".

.PARAMETER AuthorEmail
git committer email. Defaults to "noreply@jpaul.me".

.EXAMPLE
# Manual sync to Gitea (token in env)
$env:GITEA_TOKEN = '...'
./scripts/sync-wiki.ps1 -WikiUrl "https://justin:$env:GITEA_TOKEN@git.jpaul.io/justin/webhook-server.wiki.git"

.EXAMPLE
# Manual sync to GitHub (gh-issued token)
$token = & gh auth token
./scripts/sync-wiki.ps1 -WikiUrl "https://x-access-token:$token@github.com/recklessop/webhook-server.wiki.git"
#>
[CmdletBinding()]
param(
[Parameter(Mandatory)][string]$WikiUrl,
[string]$AuthorName = 'Webhook Server Wiki Sync',
[string]$AuthorEmail = 'noreply@jpaul.me'
)

$ErrorActionPreference = 'Stop'
$repoRoot = Split-Path -Parent $PSScriptRoot
$docsDir = Join-Path $repoRoot 'docs'
$workDir = Join-Path $env:TEMP ("webhook-wiki-{0}" -f ([guid]::NewGuid().ToString('N').Substring(0, 8)))

# Source path (relative to docs/) -> wiki page slug. Order matters for the sidebar.
$mapping = [ordered]@{}
$mapping.Add('README.md', 'Home')
$mapping.Add('concepts.md', 'Concepts')
$mapping.Add('installation.md', 'Installation')
$mapping.Add('upgrading.md', 'Upgrading')
$mapping.Add('uninstalling.md', 'Uninstalling')
$mapping.Add('runas-modes.md', 'Run-As-Modes')
$mapping.Add('service-account-and-ad.md', 'Service-Account-and-AD')
$mapping.Add('network-and-security.md', 'Network-and-Security')
$mapping.Add('troubleshooting.md', 'Troubleshooting')
$mapping.Add('recipes/zerto-pre-post-scripts.md', 'Recipe-Zerto-Failover')
$mapping.Add('recipes/github-style-hmac.md', 'Recipe-GitHub-HMAC')
$mapping.Add('recipes/ui-on-desktop.md', 'Recipe-UI-on-Desktop')

function Rewrite-Links([string]$content) {
foreach ($m in $mapping.GetEnumerator()) {
# Match (path/to/file.md) and (path/to/file.md#anchor) inside markdown
# link parens. The lookbehind ensures we're consuming a real link target.
$escaped = [regex]::Escape($m.Key)
$content = [regex]::Replace($content,
"\(\.?\.?/?$escaped(\#[^)\s]*)?\)",
"($($m.Value)`$1)")
}
# Also clean up doubled prefixes like "../../docs/" or "../" pointers that
# sometimes appear in cross-folder relative links from docs/recipes/.
return $content
}

function New-Sidebar() {
$lines = @()
$lines += "[Home](Home)"
$lines += ""
$lines += "## Topical"
foreach ($key in @('concepts.md','installation.md','upgrading.md','uninstalling.md','runas-modes.md','service-account-and-ad.md','network-and-security.md','troubleshooting.md')) {
$slug = $mapping[$key]
$lines += "- [$($slug -replace '-', ' ')]($slug)"
}
$lines += ""
$lines += "## Recipes"
foreach ($key in @('recipes/zerto-pre-post-scripts.md','recipes/github-style-hmac.md','recipes/ui-on-desktop.md')) {
$slug = $mapping[$key]
$lines += "- [$($slug -replace '^Recipe-' -replace '-', ' ')]($slug)"
}
return ($lines -join "`n")
}

# 1. Clone the wiki.
Write-Host "Cloning wiki to $workDir..."
git clone --quiet $WikiUrl $workDir
if ($LASTEXITCODE -ne 0) {
throw "git clone failed. Has the wiki been initialized? Visit the repo's Wiki tab and create the first page via the UI before running this script."
}

try {
Push-Location $workDir
try {
# 2. Wipe existing markdown so removed source files vanish from the wiki.
Get-ChildItem -Filter "*.md" -Force | Remove-Item -Force

# 3. Copy + transform each source file.
$written = 0
foreach ($entry in $mapping.GetEnumerator()) {
$src = Join-Path $docsDir $entry.Key
$dst = Join-Path $workDir "$($entry.Value).md"
if (-not (Test-Path $src)) {
Write-Warning "Source missing, skipping: $src"
continue
}
$content = Get-Content -LiteralPath $src -Raw
$content = Rewrite-Links $content
Set-Content -LiteralPath $dst -Value $content -Encoding utf8 -NoNewline
$written++
}
Write-Host "Wrote $written markdown pages."

# 4. Sidebar
Set-Content -LiteralPath (Join-Path $workDir '_Sidebar.md') -Value (New-Sidebar) -Encoding utf8 -NoNewline

# 5. Commit + push if anything actually changed.
git add -A
$changes = git status --porcelain
if (-not $changes) {
Write-Host "Wiki already up to date."
return
}
$sha = git -C $repoRoot rev-parse --short HEAD
git -c "user.name=$AuthorName" -c "user.email=$AuthorEmail" commit -q -m "Sync from docs/ at $sha"
git push --quiet
Write-Host "Pushed updated wiki."
}
finally { Pop-Location }
}
finally {
Remove-Item -Recurse -Force $workDir -ErrorAction SilentlyContinue
}
Loading