Merge pull request 'ci(release): publish the overlay as a release tarball with a manifest' (#7) from ci/overlay-release-workflow into main
Some checks failed
Release overlay / release (push) Failing after 10s
Some checks failed
Release overlay / release (push) Failing after 10s
Reviewed-on: #7 Reviewed-by: Colby Whitlock <whitlocktech@gmail.com>
This commit is contained in:
337
.gitea/workflows/release.yml
Normal file
337
.gitea/workflows/release.yml
Normal file
@@ -0,0 +1,337 @@
|
|||||||
|
# Automated release for the deployable ServUO overlay.
|
||||||
|
#
|
||||||
|
# Trigger: every push to `main` (i.e. every merged PR).
|
||||||
|
#
|
||||||
|
# Why this exists: the Runic Gateway installer deploys the plugin from a release
|
||||||
|
# tarball, not from git — the shard host gets no git and no Gitea credentials
|
||||||
|
# (docs/installer/PLAN.md §1, §5 Phase 0.1). Until this workflow, `link` was the
|
||||||
|
# only repo that published releases, so there was nothing for the installer to
|
||||||
|
# fetch. This is Phase 0 item 1.
|
||||||
|
#
|
||||||
|
# Flow (two conceptual halves, kept separate on purpose):
|
||||||
|
#
|
||||||
|
# ┌── RELEASE ENGINE (language-agnostic) ─────────────────────────────┐
|
||||||
|
# │ reads: latest v* git tag + conventional-commit subjects │
|
||||||
|
# │ produces: next version, changelog, and (at the end) the release │
|
||||||
|
# └───────────────────────────────────────────────────────────────────┘
|
||||||
|
# ┌── OVERLAY ADAPTER (the only repo-specific part) ──────────────────┐
|
||||||
|
# │ consumes: the version │
|
||||||
|
# │ produces: runicgateway-overlay-<ver>.tar.gz + SHA256SUMS │
|
||||||
|
# └───────────────────────────────────────────────────────────────────┘
|
||||||
|
#
|
||||||
|
# The engine is `link/.gitea/workflows/release.yml`'s, reused as its own header
|
||||||
|
# anticipated — the plan and release steps consume only {version, changelog,
|
||||||
|
# artifacts} and know nothing about what is inside the artifacts.
|
||||||
|
#
|
||||||
|
# ── Three differences from link's copy, all forced by this repo ──────────────
|
||||||
|
#
|
||||||
|
# 1. NO BUILD. The plugin ships as C# source and ServUO compiles it at boot; it
|
||||||
|
# needs ServUO reference assemblies, so there is no way to compile it here.
|
||||||
|
# The build gates are replaced by the structural gates below, which is the
|
||||||
|
# most this repo can honestly assert about an artifact.
|
||||||
|
#
|
||||||
|
# 2. NO BUMP COMMIT, and so no push to `main`. link has to write the version
|
||||||
|
# into Cargo.toml because the binary embeds it; a tarball embeds nothing but
|
||||||
|
# the manifest.json this job generates, so the git tag IS the version. That
|
||||||
|
# removes a failure mode outright: this workflow never needs `main` to accept
|
||||||
|
# a direct push, so no branch-protection exception is required for it.
|
||||||
|
#
|
||||||
|
# 3. A MANIFEST. The tarball carries manifest.json — version, commit, declared
|
||||||
|
# protocol version, ServUO compatibility, and a SHA256 for every file. The
|
||||||
|
# installer needs it because the plugin announces no version on the wire and
|
||||||
|
# none is queryable before ServUO boots (PLAN.md §2.6): the manifest is the
|
||||||
|
# only thing that lets the bundle CI verify sidecar/overlay protocol
|
||||||
|
# agreement BEFORE an operator installs the pair (PLAN.md §7.1, gate 1).
|
||||||
|
#
|
||||||
|
# Version bump (conventional commits since the last v* tag):
|
||||||
|
# feat!: / BREAKING CHANGE -> major feat: -> minor fix|perf: -> patch
|
||||||
|
# nothing releasable -> no release is cut (a docs:/chore:-only merge
|
||||||
|
# deliberately does NOT cut one — PLAN.md §7.3)
|
||||||
|
# (first ever run, no tag) -> releases SEED_VERSION below
|
||||||
|
#
|
||||||
|
# Prerequisites (Settings → Actions → Secrets on RunicGateway/servuo-plugins):
|
||||||
|
# REGISTRY_TOKEN — Gitea access token with `write:repository`, to push the
|
||||||
|
# tag and create the release.
|
||||||
|
# REGISTRY_USER — the Gitea username that token belongs to.
|
||||||
|
#
|
||||||
|
# TODO (Phase 0 item 3): once the installer repo's bundle workflow exists, append
|
||||||
|
# a final step here that POSTs to its workflow-dispatch endpoint, so a new
|
||||||
|
# overlay release recomposes the bundle immediately instead of waiting for the
|
||||||
|
# nightly cron (PLAN.md §7.2). Deliberately absent until there is something to
|
||||||
|
# dispatch — a step that 404s every release is worse than no step.
|
||||||
|
|
||||||
|
name: Release overlay
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
workflow_dispatch: {}
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: release-overlay
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
env:
|
||||||
|
GITEA_HOST: gitea.whitlocktech.com
|
||||||
|
REPO: RunicGateway/servuo-plugins
|
||||||
|
# Artifact naming per PLAN.md §3.
|
||||||
|
ARTIFACT: runicgateway-overlay
|
||||||
|
# Used only for the very first release, when no v* tag exists yet. Matches the
|
||||||
|
# house style set by link (pre-1.0; the release version is independent of the
|
||||||
|
# protocol version, which lives in overlay.toml).
|
||||||
|
SEED_VERSION: "0.1.0"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out full history (need tags + commit log for the bump)
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
# ── RELEASE ENGINE: decide the next version + changelog ──────────────
|
||||||
|
- name: Plan the release (version + changelog)
|
||||||
|
id: plan
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
mkdir -p dist
|
||||||
|
git fetch --tags --force >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
LAST_TAG="$(git describe --tags --match 'v*' --abbrev=0 2>/dev/null || true)"
|
||||||
|
if [ -n "$LAST_TAG" ]; then RANGE="${LAST_TAG}..HEAD"; else RANGE="HEAD"; fi
|
||||||
|
|
||||||
|
SUBJECTS="$(git log --no-merges --format='%s' $RANGE || true)"
|
||||||
|
BODIES="$(git log --no-merges --format='%B' $RANGE || true)"
|
||||||
|
|
||||||
|
BUMP=none
|
||||||
|
if echo "$BODIES" | grep -qE 'BREAKING[ -]CHANGE' ; then BUMP=major; fi
|
||||||
|
if echo "$SUBJECTS" | grep -qE '^[a-z]+(\([^)]+\))?!:' ; then BUMP=major; fi
|
||||||
|
if [ "$BUMP" = none ] && echo "$SUBJECTS" | grep -qE '^feat(\([^)]+\))?:' ; then BUMP=minor; fi
|
||||||
|
if [ "$BUMP" = none ] && echo "$SUBJECTS" | grep -qE '^(fix|perf)(\([^)]+\))?:'; then BUMP=patch; fi
|
||||||
|
|
||||||
|
bump() { # <x.y.z> <major|minor|patch> -> bumped
|
||||||
|
IFS=. read -r MA MI PA <<< "$1"
|
||||||
|
case "$2" in
|
||||||
|
major) echo "$((MA+1)).0.0" ;;
|
||||||
|
minor) echo "${MA}.$((MI+1)).0" ;;
|
||||||
|
patch) echo "${MA}.${MI}.$((PA+1))" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
RELEASE=true
|
||||||
|
if [ -z "$LAST_TAG" ]; then
|
||||||
|
VERSION="$SEED_VERSION" # first release: seed
|
||||||
|
elif [ "$BUMP" = none ]; then
|
||||||
|
RELEASE=false # no feat/fix/breaking since last tag
|
||||||
|
VERSION="${LAST_TAG#v}"
|
||||||
|
else
|
||||||
|
VERSION="$(bump "${LAST_TAG#v}" "$BUMP")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if git rev-parse -q --verify "refs/tags/v${VERSION}" >/dev/null; then
|
||||||
|
echo "Tag v${VERSION} already exists — nothing to release."
|
||||||
|
RELEASE=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "## ${ARTIFACT} v${VERSION}"
|
||||||
|
echo
|
||||||
|
FEATS="$(echo "$SUBJECTS" | grep -E '^feat' || true)"
|
||||||
|
FIXES="$(echo "$SUBJECTS" | grep -E '^(fix|perf)' || true)"
|
||||||
|
[ -n "$FEATS" ] && { echo "### Features"; echo "$FEATS" | sed 's/^/- /'; echo; }
|
||||||
|
[ -n "$FIXES" ] && { echo "### Fixes"; echo "$FIXES" | sed 's/^/- /'; echo; }
|
||||||
|
echo "### All changes"
|
||||||
|
if [ -n "$LAST_TAG" ]; then echo "Since ${LAST_TAG}:"; fi
|
||||||
|
echo "$SUBJECTS" | sed 's/^/- /'
|
||||||
|
} > dist/CHANGELOG.md
|
||||||
|
|
||||||
|
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "tag=v${VERSION}" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "release=${RELEASE}" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "bump=${BUMP}" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "==> release=${RELEASE} version=${VERSION} bump=${BUMP} last_tag=${LAST_TAG:-<none>}"
|
||||||
|
|
||||||
|
- name: Install jq
|
||||||
|
if: ${{ steps.plan.outputs.release == 'true' }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
command -v jq >/dev/null 2>&1 && exit 0
|
||||||
|
SUDO=""; [ "$(id -u)" -ne 0 ] && SUDO="sudo"
|
||||||
|
$SUDO apt-get update -qq
|
||||||
|
$SUDO apt-get install -y -qq --no-install-recommends jq
|
||||||
|
|
||||||
|
# ── OVERLAY ADAPTER: gates ───────────────────────────────────────────
|
||||||
|
# There is no compiler to run, so these assert the things that CAN be
|
||||||
|
# checked without a ServUO tree — and each one has actually been a way to
|
||||||
|
# ship a broken overlay:
|
||||||
|
#
|
||||||
|
# • overlay/ mirrors the server root; if Bridge.cfg or the Bridge scripts
|
||||||
|
# go missing the deploy silently no-ops (PLAN.md §2.1).
|
||||||
|
# • overlay/Scripts/Scripts.csproj is Phase 0 of the plugin itself — it
|
||||||
|
# overwrites a stock file to fix ServUO's silent script-build bug. An
|
||||||
|
# overlay shipped without it installs code that never compiles, and
|
||||||
|
# ServUO reports success anyway.
|
||||||
|
# • a malformed .patch is invisible until an operator runs the patch tier
|
||||||
|
# on their live shard. `git apply --stat` parses the diff without
|
||||||
|
# needing the target files present.
|
||||||
|
# • each patch's companion .cs must exist, since it references symbols
|
||||||
|
# the patch introduces and is meaningless without it (PLAN.md §2.2).
|
||||||
|
- name: Validate the overlay and patch tier
|
||||||
|
if: ${{ steps.plan.outputs.release == 'true' }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
fail() { echo "::error::$*"; exit 1; }
|
||||||
|
|
||||||
|
[ -f overlay/Config/Bridge.cfg ] || fail "overlay/Config/Bridge.cfg is missing"
|
||||||
|
[ -f overlay/Scripts/Scripts.csproj ] || fail "overlay/Scripts/Scripts.csproj is missing (the silent-build-bug fix)"
|
||||||
|
[ -d overlay/Scripts/Custom/Bridge ] || fail "overlay/Scripts/Custom/Bridge/ is missing"
|
||||||
|
|
||||||
|
CS_COUNT="$(find overlay/Scripts/Custom/Bridge -name '*.cs' | wc -l)"
|
||||||
|
[ "$CS_COUNT" -gt 0 ] || fail "overlay/Scripts/Custom/Bridge/ contains no .cs files"
|
||||||
|
echo "overlay: ${CS_COUNT} bridge script(s)"
|
||||||
|
|
||||||
|
for p in patches/*.patch; do
|
||||||
|
[ -e "$p" ] || fail "patches/ contains no .patch files"
|
||||||
|
echo "--- ${p}"
|
||||||
|
git apply --stat "$p" || fail "${p} is not a parseable unified diff"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Companion files that can only be copied after their patch lands.
|
||||||
|
for f in patches/BridgeVendorSale.cs patches/BridgeModerationAudit.cs; do
|
||||||
|
[ -f "$f" ] || fail "${f} is missing (a patch's companion source)"
|
||||||
|
done
|
||||||
|
|
||||||
|
[ -f overlay.toml ] || fail "overlay.toml is missing (protocol + ServUO declarations)"
|
||||||
|
|
||||||
|
# ── OVERLAY ADAPTER: stage, manifest, package ────────────────────────
|
||||||
|
# The tarball has a FIXED top-level directory (runicgateway-overlay/), not a
|
||||||
|
# versioned one: the installer extracts and then looks for overlay/,
|
||||||
|
# patches/ and manifest.json at known paths, and a version-dependent prefix
|
||||||
|
# would make it parse the very version it is trying to read.
|
||||||
|
#
|
||||||
|
# tar flags pin ownership, mtime and member order so the same tree produces
|
||||||
|
# a byte-identical tarball — a checksum that changes only when content
|
||||||
|
# changes is worth more than one that changes every run.
|
||||||
|
- name: Build manifest.json and the release tarball
|
||||||
|
if: ${{ steps.plan.outputs.release == 'true' }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
VERSION="${{ steps.plan.outputs.version }}"
|
||||||
|
STAGE="dist/stage/${ARTIFACT}"
|
||||||
|
|
||||||
|
mkdir -p "${STAGE}"
|
||||||
|
cp -r overlay "${STAGE}/overlay"
|
||||||
|
cp -r patches "${STAGE}/patches"
|
||||||
|
|
||||||
|
# Declarations from overlay.toml. Read, don't hardcode — the point of
|
||||||
|
# that file is that the protocol number lives in one place.
|
||||||
|
PROTOCOL="$(grep -m1 -E '^protocol[[:space:]]*=' overlay.toml | sed -E 's/[^0-9]//g')"
|
||||||
|
MIN_SERVUO="$(grep -m1 -E '^min_servuo_version[[:space:]]*=' overlay.toml | sed -E 's/.*"([^"]+)".*/\1/')"
|
||||||
|
PATCHED_AGAINST="$(grep -m1 -E '^patches_verified_against[[:space:]]*=' overlay.toml | sed -E 's/.*"([^"]+)".*/\1/')"
|
||||||
|
[ -n "$PROTOCOL" ] || { echo "::error::could not read protocol from overlay.toml"; exit 1; }
|
||||||
|
[ -n "$MIN_SERVUO" ] || { echo "::error::could not read min_servuo_version from overlay.toml"; exit 1; }
|
||||||
|
[ -n "$PATCHED_AGAINST" ] || { echo "::error::could not read patches_verified_against from overlay.toml"; exit 1; }
|
||||||
|
echo "==> protocol=${PROTOCOL} min_servuo=${MIN_SERVUO} patches_verified_against=${PATCHED_AGAINST}"
|
||||||
|
|
||||||
|
# Per-file SHA256 of everything shipped, as a {path: sha} object. The
|
||||||
|
# installer records these in install.json so a later `doctor` can tell
|
||||||
|
# "operator edited a deployed file" from "the overlay drifted".
|
||||||
|
# The `\*?` is not paranoia: sha256sum marks binary mode by prefixing the
|
||||||
|
# path with `*` (`<hash> *path`) instead of the two-space text-mode
|
||||||
|
# separator. Coreutils on Linux defaults to text mode, but a build host
|
||||||
|
# that doesn't would otherwise put a leading `*` on EVERY key here and
|
||||||
|
# silently produce a manifest whose paths match nothing.
|
||||||
|
FILES="$(cd "${STAGE}" \
|
||||||
|
&& find overlay patches -type f -print0 | LC_ALL=C sort -z | xargs -0 sha256sum \
|
||||||
|
| jq -R -s '
|
||||||
|
split("\n") | map(select(length > 0))
|
||||||
|
| map(capture("^(?<sha>[0-9a-f]+)[ \t]+\\*?(?<path>.+)$"))
|
||||||
|
| map({ (.path): .sha }) | add')"
|
||||||
|
|
||||||
|
jq -n \
|
||||||
|
--arg component "servuo-plugins-overlay" \
|
||||||
|
--arg version "${VERSION}" \
|
||||||
|
--arg commit "${GITHUB_SHA}" \
|
||||||
|
--arg repo "${REPO}" \
|
||||||
|
--argjson protocol "${PROTOCOL}" \
|
||||||
|
--arg min_servuo "${MIN_SERVUO}" \
|
||||||
|
--arg patched_against "${PATCHED_AGAINST}" \
|
||||||
|
--argjson files "${FILES}" \
|
||||||
|
'{
|
||||||
|
component: $component,
|
||||||
|
version: $version,
|
||||||
|
commit: $commit,
|
||||||
|
repo: $repo,
|
||||||
|
protocol: $protocol,
|
||||||
|
servuo: {
|
||||||
|
min_version: $min_servuo,
|
||||||
|
patches_verified_against: $patched_against
|
||||||
|
},
|
||||||
|
files: $files
|
||||||
|
}' > "${STAGE}/manifest.json"
|
||||||
|
|
||||||
|
echo "----- manifest.json (files elided) -----"
|
||||||
|
jq 'del(.files) + {file_count: (.files | length)}' "${STAGE}/manifest.json"
|
||||||
|
|
||||||
|
TARBALL="${ARTIFACT}-${VERSION}.tar.gz"
|
||||||
|
tar --sort=name --mtime='UTC 1970-01-01' \
|
||||||
|
--owner=0 --group=0 --numeric-owner \
|
||||||
|
-czf "dist/${TARBALL}" -C dist/stage "${ARTIFACT}"
|
||||||
|
|
||||||
|
( cd dist && sha256sum "${TARBALL}" > SHA256SUMS )
|
||||||
|
echo "tarball=${TARBALL}" >> "$GITHUB_OUTPUT"
|
||||||
|
ls -l dist && echo "----" && cat dist/SHA256SUMS
|
||||||
|
id: package
|
||||||
|
|
||||||
|
# ── RELEASE ENGINE: tag ──────────────────────────────────────────────
|
||||||
|
# Tag only — no bump commit, so `main` is never pushed to (see header).
|
||||||
|
- name: Push the release tag
|
||||||
|
if: ${{ steps.plan.outputs.release == 'true' }}
|
||||||
|
env:
|
||||||
|
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
|
||||||
|
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
TAG="${{ steps.plan.outputs.tag }}"
|
||||||
|
# Secrets can arrive with a trailing newline (depending on how they were
|
||||||
|
# pasted); a stray CR/LF corrupts the remote URL ("credential url cannot
|
||||||
|
# be parsed"). Strip line breaks before building the URL.
|
||||||
|
CI_USER="$(printf '%s' "${REGISTRY_USER}" | tr -d '\r\n')"
|
||||||
|
CI_TOKEN="$(printf '%s' "${REGISTRY_TOKEN}" | tr -d '\r\n')"
|
||||||
|
git config user.name "servuo-plugins-ci"
|
||||||
|
git config user.email "ci@whitlocktech.com"
|
||||||
|
git remote set-url origin \
|
||||||
|
"https://${CI_USER}:${CI_TOKEN}@${GITEA_HOST}/${REPO}.git"
|
||||||
|
git tag "${TAG}"
|
||||||
|
git push origin "${TAG}"
|
||||||
|
|
||||||
|
# ── RELEASE ENGINE: create the Gitea release + upload assets ─────────
|
||||||
|
- name: Create Gitea release and upload assets
|
||||||
|
if: ${{ steps.plan.outputs.release == 'true' }}
|
||||||
|
env:
|
||||||
|
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
TAG="${{ steps.plan.outputs.tag }}"
|
||||||
|
TARBALL="${{ steps.package.outputs.tarball }}"
|
||||||
|
API="https://${GITEA_HOST}/api/v1/repos/${REPO}"
|
||||||
|
BODY="$(cat dist/CHANGELOG.md)"
|
||||||
|
# Same newline hygiene as the tag step: a stray CR/LF in the token would
|
||||||
|
# corrupt the Authorization header.
|
||||||
|
CI_TOKEN="$(printf '%s' "${REGISTRY_TOKEN}" | tr -d '\r\n')"
|
||||||
|
|
||||||
|
REL_ID="$(curl -sSf -X POST "${API}/releases" \
|
||||||
|
-H "Authorization: token ${CI_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "$(jq -n --arg tag "$TAG" --arg body "$BODY" \
|
||||||
|
'{tag_name:$tag, name:$tag, body:$body, draft:false, prerelease:false}')" \
|
||||||
|
| jq -r '.id')"
|
||||||
|
echo "Created release ${TAG} (id=${REL_ID})"
|
||||||
|
|
||||||
|
for f in "${TARBALL}" SHA256SUMS; do
|
||||||
|
curl -sSf -X POST "${API}/releases/${REL_ID}/assets?name=${f}" \
|
||||||
|
-H "Authorization: token ${CI_TOKEN}" \
|
||||||
|
-F "attachment=@dist/${f}" >/dev/null
|
||||||
|
echo " uploaded ${f}"
|
||||||
|
done
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,3 +7,4 @@ obj/
|
|||||||
*.exe
|
*.exe
|
||||||
*.pdb
|
*.pdb
|
||||||
*.log
|
*.log
|
||||||
|
dist/
|
||||||
|
|||||||
55
README.md
55
README.md
@@ -27,6 +27,8 @@ integration guide, protocol spec, research — with full history preserved).
|
|||||||
| `patches/` | Unified diffs against stock ServUO for files we must modify rather than add. |
|
| `patches/` | Unified diffs against stock ServUO for files we must modify rather than add. |
|
||||||
| `tools/` | Never deployed. Test scaffolding (C# probes + PowerShell stub sidecars) and anything else that must not reach a server. |
|
| `tools/` | Never deployed. Test scaffolding (C# probes + PowerShell stub sidecars) and anything else that must not reach a server. |
|
||||||
| `deploy.ps1` | Copies `overlay/` into a server root. `-Verify` diffs instead of writing. |
|
| `deploy.ps1` | Copies `overlay/` into a server root. `-Verify` diffs instead of writing. |
|
||||||
|
| `overlay.toml` | Release metadata: the wire-protocol version this overlay speaks, and its ServUO compatibility. Read by CI into the release manifest — see [Releases](#releases). |
|
||||||
|
| `.gitea/workflows/release.yml` | Publishes `runicgateway-overlay-<ver>.tar.gz` on every merge to `main`. |
|
||||||
| [INTEGRATION.md](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/link/INTEGRATION.md) | **Website integration guide** — the WebSocket feed, REST endpoints, auth, event catalog, and examples. |
|
| [INTEGRATION.md](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/link/INTEGRATION.md) | **Website integration guide** — the WebSocket feed, REST endpoints, auth, event catalog, and examples. |
|
||||||
| [PLAN.md](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/link/PLAN.md) | Implementation plan, measured performance budget, and the full data catalog. |
|
| [PLAN.md](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/link/PLAN.md) | Implementation plan, measured performance budget, and the full data catalog. |
|
||||||
| [RESEARCH.md](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/link/RESEARCH.md) | Original source-level research. Partly superseded — see the corrections table in `PLAN.md` §8. |
|
| [RESEARCH.md](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/link/RESEARCH.md) | Original source-level research. Partly superseded — see the corrections table in `PLAN.md` §8. |
|
||||||
@@ -41,8 +43,9 @@ The two are deployed **together** but built **independently**:
|
|||||||
|
|
||||||
- **This plugin** is deployed as *source* — `deploy.ps1` copies `overlay/` into the ServUO server
|
- **This plugin** is deployed as *source* — `deploy.ps1` copies `overlay/` into the ServUO server
|
||||||
root, and ServUO compiles it at boot (`Scripts.csproj`; see [Phase 0](#phase-0--what-it-fixes)).
|
root, and ServUO compiles it at boot (`Scripts.csproj`; see [Phase 0](#phase-0--what-it-fixes)).
|
||||||
There is no separate build artifact and no CI build — it cannot be compiled standalone without the
|
There is **no CI build** — it cannot be compiled standalone without the ServUO reference
|
||||||
ServUO reference assemblies.
|
assemblies. CI does publish a *source* tarball for the installer to fetch; see
|
||||||
|
[Releases](#releases).
|
||||||
- **The sidecar** is a standalone Rust binary, released from its own repo.
|
- **The sidecar** is a standalone Rust binary, released from its own repo.
|
||||||
|
|
||||||
The **only** coupling is the loopback JSON protocol (the shard dials out to the sidecar on
|
The **only** coupling is the loopback JSON protocol (the shard dials out to the sidecar on
|
||||||
@@ -59,6 +62,54 @@ without the sidecar running.
|
|||||||
.\deploy.ps1 -ServerPath <servuo> # write
|
.\deploy.ps1 -ServerPath <servuo> # write
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`deploy.ps1` is the **developer-facing** tool and stays that way. Operators get the
|
||||||
|
[Runic Gateway installer](https://gitea.whitlocktech.com/RunicGateway/installer), which does the
|
||||||
|
same sync cross-platform from the release tarball below.
|
||||||
|
|
||||||
|
## Releases
|
||||||
|
|
||||||
|
Every merge to `main` that carries a releasable conventional commit (`feat:`, `fix:`, `perf:`, or a
|
||||||
|
breaking change — a `docs:`/`chore:`-only merge deliberately cuts nothing) publishes a Gitea release:
|
||||||
|
|
||||||
|
```
|
||||||
|
runicgateway-overlay-<ver>.tar.gz
|
||||||
|
└── runicgateway-overlay/
|
||||||
|
├── manifest.json
|
||||||
|
├── overlay/ # exactly what deploy.ps1 would copy
|
||||||
|
└── patches/ # the opt-in stock-file diffs + their companion sources
|
||||||
|
SHA256SUMS
|
||||||
|
```
|
||||||
|
|
||||||
|
This is a **source** tarball, not a build — nothing here is compiled. It exists so the installer can
|
||||||
|
deploy the plugin onto a shard host that has no git and no Gitea credentials.
|
||||||
|
|
||||||
|
`manifest.json` is what makes the tarball self-describing:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"component": "servuo-plugins-overlay",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"commit": "968b526…",
|
||||||
|
"protocol": 3,
|
||||||
|
"servuo": { "min_version": "57.4", "patches_verified_against": "57.4" },
|
||||||
|
"files": { "overlay/Config/Bridge.cfg": "32718424…", … }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- **`protocol`** comes from `overlay.toml` and is the plugin half of the compatibility contract. The
|
||||||
|
plugin announces no version on the wire and none is queryable before ServUO boots, so this
|
||||||
|
declaration is the only way the installer can check it against the sidecar's `PROTOCOL_VERSION`
|
||||||
|
*before* an operator installs the pair. **When the protocol changes, bump it in the same PR that
|
||||||
|
changes the emitters.**
|
||||||
|
- **`files`** carries a SHA256 per shipped file, so a deployment can later tell "an operator edited
|
||||||
|
this" from "the overlay moved on".
|
||||||
|
|
||||||
|
The version is derived from git tags — there is no version to maintain by hand and no bump commit,
|
||||||
|
so this workflow never pushes to `main`.
|
||||||
|
|
||||||
|
The tarball is byte-reproducible for a given tree (`tar --sort=name`, pinned mtime and ownership), so
|
||||||
|
its checksum changes only when its contents do.
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
| Phase | State |
|
| Phase | State |
|
||||||
|
|||||||
42
overlay.toml
Normal file
42
overlay.toml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# Release metadata for the deployable overlay.
|
||||||
|
#
|
||||||
|
# Consumed by .gitea/workflows/release.yml, which folds these values into the
|
||||||
|
# manifest.json shipped inside runicgateway-overlay-<ver>.tar.gz. The Runic
|
||||||
|
# Gateway installer reads that manifest to decide what it is deploying and
|
||||||
|
# whether it is compatible with the sidecar it is about to install
|
||||||
|
# (docs/installer/PLAN.md §5 Phase 0, §7.1).
|
||||||
|
#
|
||||||
|
# There is deliberately NO version key here. The release version is derived from
|
||||||
|
# git tags and conventional commits by the release workflow, so there is no bump
|
||||||
|
# commit to keep in sync and no way for this file to disagree with the tag.
|
||||||
|
|
||||||
|
# ── The loopback wire-protocol version this overlay speaks ───────────────────
|
||||||
|
#
|
||||||
|
# This is the plugin half of the compatibility contract. It MUST equal the
|
||||||
|
# sidecar's PROTOCOL_VERSION (link/sidecar/src/main.rs) for a deployment to
|
||||||
|
# work: the sidecar rejects a mismatch with 409 rather than mis-parsing.
|
||||||
|
#
|
||||||
|
# The C# plugin has no queryable version before ServUO boots — it does not
|
||||||
|
# announce one on the wire — so this declaration is the only thing that lets the
|
||||||
|
# installer's bundle CI check the pair BEFORE an operator installs them
|
||||||
|
# (docs/installer/PLAN.md §2.6, §7.1 gate 1). Keeping it honest is therefore a
|
||||||
|
# manual duty: when the protocol changes, bump it here in the same PR that
|
||||||
|
# changes the emitters, exactly as link bumps PROTOCOL_VERSION.
|
||||||
|
#
|
||||||
|
# Current: 3 — see docs/link/v3.md (world.ruleset, points.board, vendor.listing).
|
||||||
|
protocol = 3
|
||||||
|
|
||||||
|
# ── ServUO compatibility ─────────────────────────────────────────────────────
|
||||||
|
#
|
||||||
|
# The base overlay (Config/Bridge.cfg + Scripts/Custom/Bridge/*.cs) only ADDS
|
||||||
|
# files and is expected to work on any reasonably current ServUO. This is the
|
||||||
|
# oldest version it is known good on.
|
||||||
|
min_servuo_version = "57.4"
|
||||||
|
|
||||||
|
# The patches/ tier is a different matter: those are unified diffs against STOCK
|
||||||
|
# ServUO files, so they are verified against exactly one version and nothing
|
||||||
|
# else. On any other version the installer skips the whole tier with a warning
|
||||||
|
# and completes the base install (docs/installer/PLAN.md §1, §2.2) — losing
|
||||||
|
# vendor.sale events and in-game moderation-audit forwarding, but never
|
||||||
|
# half-patching an unknown tree.
|
||||||
|
patches_verified_against = "57.4"
|
||||||
Reference in New Issue
Block a user