Github wiki content is generally not allowed to be indexed by third-parties like Google Search. GeoDmsWikiToStaticHtml converts public Github wikis to static html content. This allows the wiki content to be duplicated neatly and served at a location that allows for indexing (also by LLM crawlers), while keeping the source in one location.
The result is served at https://www.geodms.nl. Major thanks to just the docs for their awesome template.
One run builds all wikis into a single _out/ tree that is deployed as one whole:
The main site links to the subsites in its navigation bar; each subsite links back. _out/sitemap.xml (with per-page last-modified dates from the wiki git history) and the older _out/sitemap.txt cover all sites — point the server's hand-placed robots.txt at https://www.geodms.nl/sitemap.xml. _out/llms.txt is a markdown index of all pages (llmstxt.org), a clean entry point for llm crawlers. _out/.well-known/security.txt states where to report security issues (RFC 9116); change the addresses in SECURITY_CONTACTS in the script.
The wikis can link to each other (and to themselves) with their normal github wiki urls, e.g. https://github.com/ObjectVision/RSopen/wiki/Beschikbaarheid: the converter rewrites those to the corresponding internal geodms.nl location, including #section anchors. Links to pages that do not exist in the target wiki are kept as github links.
Adding a wiki: the wiki needs a Home.md and a _Sidebar.md. Then:
- add an entry to
SITESinconvert_wiki_to_static_html.py(name, wiki git url, baseurl); - create
template/_config_<name>.ymlwith itstitle,baseurland links (copy an existing overlay); - add the name to
keep_filesintemplate/_config.yml, and optionally anav_external_linksentry there so the main site links to it.
The Github Actions workflow .github/workflows/build-and-deploy.yml builds all sites and deploys them to geodms.nl over FTPS. No local installation is needed. On a public repository Github Actions is free of charge.
It runs:
- nightly (03:17 UTC) — change the
cron:line in the workflow to alter the frequency, e.g."17 3 * * 1"for weekly on Monday; - on demand: Actions tab -> Build and deploy geodms.nl -> Run workflow. Tick dry run to only build and inspect the result as a downloadable artifact, without deploying.
- on every wiki edit of the GeoDMS wiki, if the optional trigger extra/wiki-updated-trigger-for-GeoDMS-repo.yml is installed in the ObjectVision/GeoDMS repository (see the comments in that file).
One-time setup, in this repository under Settings -> Secrets and variables -> Actions:
| Kind | Name | Value |
|---|---|---|
| Secret | FTP_SERVER |
hostname of the webserver |
| Secret | FTP_USERNAME |
ftp account name |
| Secret | FTP_PASSWORD |
ftp account password |
| Variable | FTP_SERVER_DIR |
optional; remote deploy dir, default public_html/ |
The deploy action (SamKirkland/FTP-Deploy-Action) keeps a state file (.ftp-deploy-sync-state.json) on the server so subsequent deploys only transfer changed files. It never deletes files it did not upload itself, so hand-placed files (e.g. .htaccess, robots.txt) are left alone; pages removed from a wiki linger on the server until removed by hand.
Requirements: Python, Ruby and the Jekyll and Bundler gems (gem install jekyll bundler, then bundle install inside template/).
python convert_wiki_to_static_html.py [--sites geodms,rsopen,...] [--serve] [--skip-clone] [--skip-jekyll]
This will, per site:
- clone its wiki repository into
wikis/<name>(--skip-clonereuses existing clones) - parse
_Sidebar.mdand use it as navigation bar - convert internal
[[ ]]image and file links to[]()format, intotemplate/docs/ - run
bundle exec jekyll buildwith_config.ymlplus the site's_config_<name>.ymloverlay (--skip-jekyllstops before this step), into_out<baseurl>
and finally write _out/sitemap.txt. With --serve the whole result is served at http://localhost:8000. --sites rebuilds a subset; the other sites' output stays untouched in _out/.
To upload a locally built site to the webserver, store a WinSCP site with the ftp credentials once and run:
.\deploy_local_winscp.ps1 -Site "<stored WinSCP site name>"
By default the left column follows the wiki's own _Sidebar.md. A site can override that with
nav/<site>.md in this repo, in exactly the same syntax. The wiki then stays as its authors
left it, while the website groups the pages the way a reader of the website needs them;
nav/geodms.md does this for the main site. Delete the file and the wiki sidebar takes over
again.
A list item that is plain text rather than a link becomes a section: the converter writes a
stub page for it and just-the-docs fills that page with the list of its children. That is how
Getting started, Reference and Development exist without a wiki page behind them.
Pages that are not in the tree are still built, still reachable by url and still in the search index; they only stay out of the menu. So a new wiki page has to be added to the tree to appear in the left column.
- Images pasted directly into the github wiki editor end up on github's attachment CDN instead of in the wiki's
images/folder. The converter downloads those (cached in_external_images/) and serves them fromassets/img/external/, so the site stays self-contained, leaks no visitor data to github, and can run a strictimg-srcpolicy. A download failure is reported and leaves the github url in place. - After every deploy the workflow notifies IndexNow of the pages whose wiki source changed in the last two days, which reaches Bing, Yandex, Seznam and Naver without needing an account (Google ignores IndexNow; it uses the sitemap). Run the workflow with indexnow_all to submit every page once. The key in
INDEXNOW_KEYis public by design and is served from the site root. - Math is rendered by MathJax 3, configured in
template/assets/js/mathjax-script-type.js(loaded before the mathjax bundle, both deferred). The wikis write inline math as$...$, which MathJax 3 does not recognise out of the box — without that config every inline formula shows up as raw LaTeX. Code and pre blocks are skipped by MathJax, and\$stays a literal dollar. - Every page gets a meta description (the snippet text in search results) extracted from its first real paragraph; a leading breadcrumb or tag line is skipped in favour of the paragraph after it. Pages that start with only tables or images fall back to the site description.
- Non-English sites set
langin their_config_<name>.yml(RSopen:lang: nl). - Github renders a markdown table that directly follows a paragraph, kramdown/Jekyll does not; the converter inserts the missing blank line so such tables render correctly here too.
- The Jekyll SEO header (canonical url, meta description) is kept in the generated pages since the whole point of this site is being indexable.
clean_html_file(..., remove_jekyll_header_part=True)can strip it again for local file-based use. template/Gemfile.lockis pinned to the Windows platform (x64-mingw-ucrt); the CI workflow therefore uses a windows runner. To build on Linux, runbundle lock --add-platform x86_64-linuxintemplate/and commit the lockfile.- Documentation content is distributed under CC BY-SA 4.0; this tool under GNU GPL-3.