The registry: coords_hud, the checks, publishing to Pages, the weekly cleanup

- mods/coords_hud.toml: the SDK's example mod, MIT, built from
  crates/horizon-mod-sdk/examples/coords-hud in Horizon.
- check.yml: a pull request's changes, each new file's checks and a trial
  run in the sandbox, the index it would publish (cargo horizon-mod from
  Horizon at HORIZON_REV).
- publish.yml: on main, new versions become releases, index.json and the
  icons go to GitHub Pages.
- cleanup.yml: staging copies nobody needs, weekly.
- CODEOWNERS, the report form, the pull request template, revoked.toml.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Ludovic MarieandClaude Opus 5.5 committed 2026-10-25 15:01:10 +01:00
1 parent 0608babaab
commit 52e1f7d0f9
12 files changed
+326

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
# Every change needs a review from one of these before it merges (branch protection:
# "Require review from Code Owners"). Add trusted contributors to the first line.
* @ludovic111 @rdbtCVS @levzzz5154 @socksprox
/.github/ @ludovic111
/revoked.toml @ludovic111
+8
View File
@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Submit a project
url: https://playhorizon.dev/mods/submit
about: Mods, modpacks and resource packs for Horizon. Each submission is reviewed.
- name: Horizon itself
url: https://github.com/ludovic111/horizon/issues
about: Bugs in the game or the launcher.
+43
View File
@@ -0,0 +1,43 @@
name: Report a project
description: Something in the registry does harm, breaks the game, isn't what it says, or isn't its author's to publish.
title: "Report: "
labels: [report]
body:
- type: markdown
attributes:
value: |
Thanks. Reviewers read every report. If it is dangerous (it deletes files, steals
accounts, spreads), say so in the first line: it can be taken down within hours, and
the launcher then turns installed copies off. Bugs in a mod go to its source repository.
- type: input
id: project
attributes:
label: Project id
description: The last part of its page's address, playhorizon.dev/mods/<id>
placeholder: coords_hud
validations:
required: true
- type: input
id: version
attributes:
label: Version
placeholder: 0.1.0, or "all"
- type: dropdown
id: why
attributes:
label: What is wrong
options:
- It does harm (files, accounts, chat it shouldn't send, anything malicious)
- It isn't what its page says
- It breaks the game or other mods
- It isn't the submitter's to publish (copyright, licence, stolen)
- Something else
validations:
required: true
- type: textarea
id: details
attributes:
label: What happened
description: Steps, what you saw, and logs if you have them (the game's log is latest.log in its folder).
validations:
required: true
+9
View File
@@ -0,0 +1,9 @@
<!-- Changing mods/<id>.toml by hand? The form at https://playhorizon.dev/mods/submit does this for you. -->
**Project:** <!-- id, and new project or update -->
- [ ] I made it, or I am allowed to publish it, under the licence the entry names.
- [ ] The `source` link has the code this file was built from.
- [ ] I ran `cargo horizon-mod check <file>` and it passed.
The file goes in the `staging` release as `<sha256>.<extension>` (the form uploads it; by hand, ask a reviewer).
+82
View File
@@ -0,0 +1,82 @@
# A pull request's checks: what it changes compared with main, each new file's
# own check (its kind's, its signature, a trial run of a mod in the sandbox), and
# the index it would publish. Runs with a read-only token and no secrets; the tool
# comes from Horizon, never from the pull request, and a submission's code only
# runs inside Horizon's mod sandbox.
name: check
on:
pull_request:
branches: [main]
permissions:
contents: read
concurrency:
group: check-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
check:
runs-on: ubuntu-latest
timeout-minutes: 30
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
HORIZON_REV: ${{ vars.HORIZON_REV || 'main' }}
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.base.sha }}
path: base
- uses: actions/checkout@v4
with:
path: head
persist-credentials: false
- uses: actions/cache@v4
with:
path: |
~/.cargo/bin
~/.cargo/horizon-rev
key: cargo-horizon-mod-${{ env.HORIZON_REV }}
# main's scripts, not the pull request's.
- run: base/scripts/install-tool.sh
- name: Who submitted it
id: author
env:
LOGIN: ${{ github.event.pull_request.user.login }}
BODY: ${{ github.event.pull_request.body }}
BOT: ${{ vars.REGISTRY_BOT || 'horizon-mods-bot' }}
run: |
# The submission form opens pull requests as the bot and names the
# GitHub account that signed in to it.
author="$LOGIN"
if [ "$LOGIN" = "$BOT" ] || [ "$LOGIN" = "${BOT}[bot]" ]; then
author="$(printf '%s' "$BODY" | sed -n 's/.*<!-- submitted-by: \([A-Za-z0-9-]*\) -->.*/\1/p' | head -1)"
fi
echo "login=${author:-$LOGIN}" >> "$GITHUB_OUTPUT"
- name: What it changes
run: |
set -o pipefail
{ echo "## Changes"; echo '```'; } >> "$GITHUB_STEP_SUMMARY"
status=0
cargo horizon-mod registry review base head --author "${{ steps.author.outputs.login }}" 2>&1 \
| tee -a "$GITHUB_STEP_SUMMARY" || status=1
echo '```' >> "$GITHUB_STEP_SUMMARY"
exit $status
- name: Download its files
run: base/scripts/fetch.sh head
- name: Check each new file
run: |
set -o pipefail
{ echo "## New files"; echo '```'; } >> "$GITHUB_STEP_SUMMARY"
status=0
cargo horizon-mod registry check base head --files files 2>&1 | tee -a "$GITHUB_STEP_SUMMARY" || status=1
echo '```' >> "$GITHUB_STEP_SUMMARY"
exit $status
- name: The index it would publish
run: |
cargo horizon-mod registry build head --files files --out index.json --generated "$(date -u +%FT%TZ)"
python3 - >> "$GITHUB_STEP_SUMMARY" <<'PY'
import json
index = json.load(open("index.json"))
print("## index.json")
for p in index["projects"]:
print(f"- {p['kind']} `{p['id']}` {p['versions'][0]['version']}")
PY
+43
View File
@@ -0,0 +1,43 @@
# The submission form uploads files to the `staging` prerelease as
# <sha256>.<extension>. Once a version is published in its own release, or when
# no open pull request lists a file any more, its staging copy goes:
# published ones after 7 days, abandoned ones after 30.
name: cleanup
on:
schedule:
- cron: "17 4 * * 1"
workflow_dispatch:
permissions:
contents: write
pull-requests: read
jobs:
cleanup:
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
steps:
- uses: actions/checkout@v4
- name: Delete staging files nobody needs
run: |
set -euo pipefail
# Hashes main lists (published), and those open pull requests add.
grep -ho 'sha256 = "[0-9a-f]\{64\}"' mods/*.toml | cut -d'"' -f2 | sort -u > published.txt || true
: > pending.txt
for pr in $(gh pr list --state open --json number --jq '.[].number'); do
gh pr diff "$pr" | grep -o '^+sha256 = "[0-9a-f]\{64\}"' | cut -d'"' -f2 >> pending.txt || true
done
now=$(date +%s)
gh api --paginate "repos/$GH_REPO/releases/tags/staging" --jq '.assets[] | [.id, .name, .created_at] | @tsv' |
while IFS=$'\t' read -r id name created; do
sha="${name%%.*}"
age=$(( (now - $(date -d "$created" +%s)) / 86400 ))
grep -qx "$sha" pending.txt && continue
if grep -qx "$sha" published.txt; then
[ "$age" -ge 7 ] || continue
else
[ "$age" -ge 30 ] || continue
fi
echo "Deleting $name ($age days)"
gh api -X DELETE "repos/$GH_REPO/releases/assets/$id"
done
+65
View File
@@ -0,0 +1,65 @@
# Merging publishes: each new version's file becomes a release asset
# (<id>-<version>/<file>), then index.json and the icons go to GitHub Pages
# (https://ludovic111.github.io/horizon-mods/index.json), which the launcher and
# playhorizon.dev/mods read. Nothing is published if any entry fails its checks.
name: publish
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: write
pages: write
id-token: write
concurrency:
group: publish
cancel-in-progress: false
jobs:
publish:
runs-on: ubuntu-latest
timeout-minutes: 30
environment:
name: github-pages
url: ${{ steps.pages.outputs.page_url }}
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
HORIZON_REV: ${{ vars.HORIZON_REV || 'main' }}
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
with:
path: |
~/.cargo/bin
~/.cargo/horizon-rev
key: cargo-horizon-mod-${{ env.HORIZON_REV }}
- run: scripts/install-tool.sh
- run: scripts/fetch.sh .
- name: Build the index
run: |
mkdir -p site
cargo horizon-mod registry build . --files files --out site/index.json --generated "$(date -u +%FT%TZ)"
- name: Publish new files as releases
run: |
cargo horizon-mod registry list . | while read -r sha id version file; do
tag="$id-$version"
gh release view "$tag" >/dev/null 2>&1 && continue
dir="$(mktemp -d)"
cp "files/$sha" "$dir/$file"
gh release create "$tag" "$dir/$file" --title "$id $version" --latest=false \
--notes "Reviewed in the registry: mods/$id.toml. SHA-256 $sha."
done
- name: The site
run: |
cp -R icons site/icons
cat > site/index.html <<'HTML'
<!doctype html><meta charset="utf-8"><title>Horizon mods</title>
<meta http-equiv="refresh" content="0; url=https://playhorizon.dev/mods">
<p><a href="https://playhorizon.dev/mods">playhorizon.dev/mods</a> · <a href="index.json">index.json</a></p>
HTML
- uses: actions/configure-pages@v5
- uses: actions/upload-pages-artifact@v3
with:
path: site
- id: pages
uses: actions/deploy-pages@v4
Binary file not shown.

After

Width:  |  Height:  |  Size: 336 B

+26
View File
@@ -0,0 +1,26 @@
id = "coords_hud"
kind = "mod"
name = "Coordinates HUD"
summary = "Your coordinates, facing and the time of day in a corner of the screen."
description = """
The SDK's example mod. It draws your block position, the direction you face and the time of day
in the top-left corner, every frame.
- `/where` puts your coordinates and dimension in chat.
- `/coords` hides or shows the panel.
It reads the game (position, health, time), draws on the HUD and writes to chat: nothing else.
"""
authors = ["Horizon"]
owners = ["ludovic111"]
license = "MIT"
source = "https://github.com/ludovic111/horizon/tree/main/crates/horizon-mod-sdk/examples/coords-hud"
icon = "icons/coords_hud.png"
[[versions]]
version = "0.1.0"
published = "2026-10-24"
game = "26.3"
file = "coords_hud-0.1.0.hmod"
sha256 = "612335e4e00d638f8b97b6c2f5c718c069c7a13811ba922557c27cb2612daf59"
changelog = "First release: the panel, `/where` and `/coords`."
+8
View File
@@ -0,0 +1,8 @@
# Files taken down. The launcher refuses to install them and turns installed copies off,
# showing the reason. One file by its sha256, or every version of an id without one.
#
# [[revoked]]
# id = "some_mod"
# sha256 = "..."
# reason = "What it did, in one sentence players will read."
# date = "2026-10-24"
+26
View File
@@ -0,0 +1,26 @@
#!/usr/bin/env bash
# Downloads every file the entries list into files/<sha256>: from its own release
# (<id>-<version>) once published, else from the `staging` prerelease, where the
# submission form uploads <sha256>.<extension>. Usage: scripts/fetch.sh [checkout]
# Needs `gh` (GH_TOKEN) and cargo-horizon-mod. FILES_FROM=<dir> reads <dir>/<file>
# instead, for a local trial.
set -euo pipefail
checkout="${1:-.}"
mkdir -p files
cargo horizon-mod registry list "$checkout" | while read -r sha id version file; do
[ -f "files/$sha" ] && continue
ext="${file##*.}"
tmp="$(mktemp -d)"
if [ -n "${FILES_FROM:-}" ]; then
cp "$FILES_FROM/$file" "$tmp/$file"
elif gh release download "$id-$version" --pattern "$file" --dir "$tmp" 2>/dev/null; then
:
elif gh release download staging --pattern "$sha.$ext" --dir "$tmp"; then
mv "$tmp/$sha.$ext" "$tmp/$file"
else
echo "::error::$id $version: $file is neither in release $id-$version nor in staging as $sha.$ext"
exit 1
fi
mv "$tmp/$file" "files/$sha"
rmdir "$tmp"
done
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
# Installs cargo-horizon-mod from Horizon at $HORIZON_REV (a commit; the repository
# variable HORIZON_REV, else main). The registry's checks are that tool's.
set -euo pipefail
rev="${HORIZON_REV:-main}"
if command -v cargo-horizon-mod >/dev/null && [ "$(cat ~/.cargo/horizon-rev 2>/dev/null)" = "$rev" ]; then
exit 0
fi
cargo install --locked --git https://github.com/ludovic111/horizon --rev "$rev" cargo-horizon-mod 2>/dev/null \
|| cargo install --locked --git https://github.com/ludovic111/horizon --branch "$rev" cargo-horizon-mod
echo "$rev" > ~/.cargo/horizon-rev