build: add preflight validator family for versions/manifest/changelog drift
Establishes the local pre-push gate. preflight.sh runs four blocks: version consistency, manifest shape (Icon plus all ImageUrls), changelog sync, plus a release build as compile-health smoke. setup-hooks.sh wires core.hooksPath to .githooks. .gitignore opens scripts/ for tracking (setup-dev-env.sh stays private). Test execution itself lives in a separate local repository and is not part of this codebase.
This commit is contained in:
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env bash
|
||||
# .githooks/pre-push — invokes preflight.sh (A/B/C/D=build).
|
||||
exec scripts/preflight.sh
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
.envrc
|
||||
!.env.example
|
||||
.vscode/
|
||||
scripts/
|
||||
scripts/setup-dev-env.sh
|
||||
|
||||
# Local test project (stays out of the published plugin repo;
|
||||
# pure-function safety net for refactor cycles)
|
||||
|
||||
@@ -145,3 +145,19 @@ I respond on weekdays during European business hours and take weekends
|
||||
and FFXIV patch days off. A pull request that sits for a few days has
|
||||
not been ignored. Pinging once after a week is fine; please do not
|
||||
ping daily.
|
||||
|
||||
## First-time setup
|
||||
|
||||
After cloning, run once:
|
||||
|
||||
```bash
|
||||
./scripts/setup-hooks.sh
|
||||
```
|
||||
|
||||
This wires `core.hooksPath` to `.githooks/`. The pre-push hook runs preflight
|
||||
(versions/manifest/changelog/build).
|
||||
|
||||
### Test suite
|
||||
|
||||
The plugin's test suite lives in a separate local repository and is not part of
|
||||
this codebase. If you need access for development, contact the maintainer.
|
||||
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
# preflight.sh — pre-push gate. Blocks A/B/C verify config drift; Block D is a
|
||||
# headless `dotnet build` to catch compile-time API drift. Test execution lives
|
||||
# in the local Build-Suite repo and is NOT part of this preflight.
|
||||
|
||||
set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
echo "==> preflight: Block A — version consistency"
|
||||
./scripts/verify-version-consistency.sh
|
||||
|
||||
echo "==> preflight: Block B — manifest shape"
|
||||
./scripts/verify-manifest-shape.sh
|
||||
|
||||
echo "==> preflight: Block C — changelog sync"
|
||||
./scripts/verify-changelog-sync.sh
|
||||
|
||||
echo "==> preflight: Block D — plugin compile health"
|
||||
dotnet build HellionChat/HellionChat.csproj --configuration Release --nologo --verbosity quiet
|
||||
|
||||
echo "==> preflight: ALL GREEN"
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
# setup-hooks.sh — installs pre-push hook via core.hooksPath. Idempotent.
|
||||
# Note: NO pre-commit hook — test execution is local-only in the Build-Suite repo,
|
||||
# so the plugin repo's pre-commit cannot run tests. Versions/manifest/changelog
|
||||
# checks happen on pre-push only.
|
||||
|
||||
set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
git config core.hooksPath .githooks
|
||||
chmod +x .githooks/pre-push
|
||||
echo "setup-hooks: core.hooksPath set to .githooks. pre-push live."
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
# verify-changelog-sync.sh — Block C.
|
||||
# Strips .0 suffix from repo.json AssemblyVersion to derive the 3-digit tag/version.
|
||||
# yaml.changelog is a single multi-line block with **Hellion Chat X.Y.Z** subblocks.
|
||||
|
||||
set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
|
||||
YAML="$ROOT/HellionChat/HellionChat.yaml"
|
||||
REPO_JSON="$ROOT/repo.json"
|
||||
FORGE_DIR="$ROOT/.github/forge-posts"
|
||||
|
||||
fail() { echo "verify-changelog-sync: FAIL — $1" >&2; exit 1; }
|
||||
ok() { echo "verify-changelog-sync: OK — $1"; }
|
||||
|
||||
VER="$(jq -r '.[0].AssemblyVersion' "$REPO_JSON" | sed -E 's/\.0$//')"
|
||||
TAG="v$VER"
|
||||
|
||||
grep -qE "^[[:space:]]*\*\*Hellion Chat ${VER}" "$YAML" \
|
||||
|| fail "$YAML changelog missing **Hellion Chat ${VER}** subblock. Fix: add the v${VER} block at the top of the changelog field."
|
||||
|
||||
jq -r '.[0].Changelog' "$REPO_JSON" | grep -qE "^[[:space:]]*\*\*Hellion Chat ${VER}" \
|
||||
|| fail "$REPO_JSON Changelog missing **Hellion Chat ${VER}** subblock. Fix: copy the yaml changelog over."
|
||||
|
||||
FORGE_FILE="$FORGE_DIR/${TAG}.md"
|
||||
[ -f "$FORGE_FILE" ] || fail "$FORGE_FILE missing. Fix: create from previous tag's file as template."
|
||||
|
||||
SUBTITLE="$(awk '/^---$/{f=!f; next} f && /^subtitle:/' "$FORGE_FILE" | sed -E 's/^subtitle:[[:space:]]*//' | tr -d '\"')"
|
||||
[ -n "$SUBTITLE" ] || fail "$FORGE_FILE frontmatter missing 'subtitle'."
|
||||
[ "${#SUBTITLE}" -le 60 ] || fail "$FORGE_FILE subtitle is ${#SUBTITLE} chars (max 60)."
|
||||
|
||||
NATUR="$(awk '/^---$/{f=!f; next} f && /^versionsnatur:/' "$FORGE_FILE" | sed -E 's/^versionsnatur:[[:space:]]*//' | tr -d '\"')"
|
||||
[ -n "$NATUR" ] || fail "$FORGE_FILE frontmatter missing 'versionsnatur'."
|
||||
[ "${#NATUR}" -le 40 ] || fail "$FORGE_FILE versionsnatur is ${#NATUR} chars (max 40)."
|
||||
|
||||
BODY="$(awk '/^---$/{f++; next} f==2' "$FORGE_FILE")"
|
||||
TITLE_LEN=$((${#VER} + 16 + ${#SUBTITLE}))
|
||||
FOOTER_LEN=80
|
||||
TOTAL=$((TITLE_LEN + ${#BODY} + FOOTER_LEN))
|
||||
[ "$TOTAL" -le 5500 ] || fail "Forge embed total ~${TOTAL} chars > 5500 cap."
|
||||
|
||||
YAML_VERSIONS="$(grep -cE '^[[:space:]]*\*\*Hellion Chat' "$YAML" || true)"
|
||||
[ "$YAML_VERSIONS" -le 4 ] || fail "$YAML changelog has $YAML_VERSIONS version subblocks (max 4). Fix: move oldest to docs/CHANGELOG.md."
|
||||
|
||||
ok "yaml/repo.json/forge-post in sync for $TAG, embed-total ~${TOTAL}/5500, $YAML_VERSIONS/4 subblocks"
|
||||
Executable
+43
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env bash
|
||||
# verify-manifest-shape.sh — Block B. Required fields in lowercase yaml,
|
||||
# DalamudApiLevel sanity in repo.json, no own DalamudPackager.targets,
|
||||
# Icon AND every ImageUrl reachable.
|
||||
|
||||
set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
|
||||
YAML="$ROOT/HellionChat/HellionChat.yaml"
|
||||
REPO_JSON="$ROOT/repo.json"
|
||||
TARGETS_OWN="$ROOT/HellionChat/DalamudPackager.targets"
|
||||
|
||||
fail() { echo "verify-manifest-shape: FAIL — $1" >&2; exit 1; }
|
||||
ok() { echo "verify-manifest-shape: OK — $1"; }
|
||||
|
||||
for FIELD in name author punchline description repo_url icon_url image_urls tags changelog; do
|
||||
grep -qE "^${FIELD}:" "$YAML" || fail "$YAML missing required field: $FIELD"
|
||||
done
|
||||
|
||||
[ -f "$TARGETS_OWN" ] && fail "Own DalamudPackager.targets at $TARGETS_OWN strips Icon/ImageUrls. DELETE it; SDK default works."
|
||||
|
||||
API_LEVEL="$(jq -r '.[0].DalamudApiLevel' "$REPO_JSON")"
|
||||
case "$API_LEVEL" in
|
||||
''|null) fail "$REPO_JSON missing DalamudApiLevel" ;;
|
||||
esac
|
||||
[[ "$API_LEVEL" =~ ^[0-9]+$ ]] || fail "$REPO_JSON DalamudApiLevel must be integer, got: $API_LEVEL"
|
||||
[ "$API_LEVEL" -ge 12 ] || fail "$REPO_JSON DalamudApiLevel=$API_LEVEL is below SDK 15 floor (12)."
|
||||
|
||||
if [ "${HOOKS_OFFLINE:-0}" != "1" ]; then
|
||||
ICON_URL="$(jq -r '.[0].IconUrl' "$REPO_JSON")"
|
||||
curl -fsI "$ICON_URL" > /dev/null || fail "IconUrl unreachable: $ICON_URL"
|
||||
ok "IconUrl reachable: $ICON_URL"
|
||||
|
||||
COUNT="$(jq -r '.[0].ImageUrls | length' "$REPO_JSON")"
|
||||
[ "$COUNT" -ge 1 ] || fail "repo.json ImageUrls is empty"
|
||||
for i in $(seq 0 $((COUNT - 1))); do
|
||||
URL="$(jq -r ".[0].ImageUrls[$i]" "$REPO_JSON")"
|
||||
curl -fsI "$URL" > /dev/null || fail "ImageUrls[$i] unreachable: $URL"
|
||||
done
|
||||
ok "$COUNT ImageUrl(s) reachable, DalamudApiLevel=$API_LEVEL"
|
||||
else
|
||||
ok "skipped URL reachability (HOOKS_OFFLINE=1), DalamudApiLevel=$API_LEVEL"
|
||||
fi
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
# verify-version-consistency.sh — Block A of preflight.
|
||||
# csproj <Version> is 3-digit SemVer; repo.json AssemblyVersion is 4-digit (.0 suffix).
|
||||
|
||||
set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
|
||||
CSPROJ="$ROOT/HellionChat/HellionChat.csproj"
|
||||
REPO_JSON="$ROOT/repo.json"
|
||||
|
||||
fail() { echo "verify-version-consistency: FAIL — $1" >&2; exit 1; }
|
||||
ok() { echo "verify-version-consistency: OK — $1"; }
|
||||
|
||||
CSPROJ_VER="$(grep -oE '<Version>[^<]+</Version>' "$CSPROJ" | head -1 | sed -E 's/<[^>]+>//g')"
|
||||
[ -n "$CSPROJ_VER" ] || fail "$CSPROJ has no <Version> element"
|
||||
|
||||
EXPECTED_4DIGIT="${CSPROJ_VER}.0"
|
||||
|
||||
REPO_VER="$(jq -r '.[0].AssemblyVersion' "$REPO_JSON")"
|
||||
[ "$REPO_VER" = "$EXPECTED_4DIGIT" ] \
|
||||
|| fail "csproj=$CSPROJ_VER expects repo.json AssemblyVersion=$EXPECTED_4DIGIT but got $REPO_VER. Fix: align in $REPO_JSON."
|
||||
|
||||
TEST_VER="$(jq -r '.[0].TestingAssemblyVersion' "$REPO_JSON")"
|
||||
[ "$TEST_VER" = "$EXPECTED_4DIGIT" ] \
|
||||
|| fail "TestingAssemblyVersion=$TEST_VER must match $EXPECTED_4DIGIT. Fix: align in $REPO_JSON."
|
||||
|
||||
TAG="v$CSPROJ_VER"
|
||||
for KEY in DownloadLinkInstall DownloadLinkUpdate DownloadLinkTesting; do
|
||||
URL="$(jq -r ".[0].$KEY" "$REPO_JSON")"
|
||||
case "$URL" in
|
||||
*"/$TAG/"*) ;;
|
||||
*) fail "$KEY=$URL does not contain tag $TAG. Fix: update $REPO_JSON $KEY to releases/download/$TAG/latest.zip." ;;
|
||||
esac
|
||||
done
|
||||
|
||||
ok "csproj=$CSPROJ_VER, repo.json=$EXPECTED_4DIGIT, tag $TAG present in DownloadLinks"
|
||||
Reference in New Issue
Block a user