diff --git a/.gitea/release-footer.md b/.gitea/release-footer.md new file mode 100644 index 0000000..b386ff1 --- /dev/null +++ b/.gitea/release-footer.md @@ -0,0 +1,37 @@ +--- + +### Install + +Add the Hellion Forge custom repository in Dalamud Settings +(`/xlsettings` → **Experimental** → **Custom Plugin Repositories**): + +```text +https://gitea.hellion-forge.cloud/JonKazama-Hellion/Craftimizer/raw/branch/main/repo.json +``` + +Save, then go to `/xlplugins` → **All Plugins** → Refresh. Forgeimizer +shows up in the list — install it like any other plugin. + +> ⚠️ **Conflict notice:** Forgeimizer refuses to load if the upstream +> Craftimizer plugin is active. Disable upstream Craftimizer in `/xlplugins` +> before enabling Forgeimizer. + +### Licence and Attribution + +MIT (same licence as upstream Craftimizer). Crafting logic, solver, +simulator, recipe data, synthesis hooks, and UI windows by +[Asriel Camora](https://github.com/WorkingRobot) — see +[NOTICE.md](https://gitea.hellion-forge.cloud/JonKazama-Hellion/Craftimizer/src/branch/main/NOTICE.md) +for the full acknowledgement and contact path. + +Hellion Forge fork-maintenance (rebrand + SDK 15 migration + conflict +detector) by Jon Kazama / Hellion Online Media. + +### Documentation + +- [README.md](https://gitea.hellion-forge.cloud/JonKazama-Hellion/Craftimizer/src/branch/main/README.md) +- [CHANGELOG.md](https://gitea.hellion-forge.cloud/JonKazama-Hellion/Craftimizer/src/branch/main/CHANGELOG.md) +- [NOTICE.md](https://gitea.hellion-forge.cloud/JonKazama-Hellion/Craftimizer/src/branch/main/NOTICE.md) +- [COPYRIGHT](https://gitea.hellion-forge.cloud/JonKazama-Hellion/Craftimizer/src/branch/main/COPYRIGHT) + +Maintained under **Hellion Forge** | [hellion-media.de](https://hellion-media.de) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml new file mode 100644 index 0000000..cf1ecb4 --- /dev/null +++ b/.gitea/workflows/build.yml @@ -0,0 +1,50 @@ +name: Build + +# Verifies that every push to main and every PR still builds against the +# current Dalamud staging branch. Does not produce release artefacts; the +# release workflow handles that on tag. +# +# Linux runner: gitea.com Cloud Actions provides ubuntu-latest. The plugin +# csproj targets net10.0-windows, but `dotnet build` cross-compiles on Linux +# as long as the Dalamud staging assemblies are present at the expected +# lookup path ($(HOME)/.xlcore/dalamud/Hooks/dev/, which Dalamud SDK 15 uses +# on Linux). + +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + +# Minimum permissions for a build-only workflow: read the repo, nothing else. +permissions: + contents: read + +jobs: + build: + name: Build (Release) + runs-on: ubuntu-latest + timeout-minutes: 15 + + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Setup .NET 10 + uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5 + with: + dotnet-version: 10.0.x + + - name: Download Dalamud staging + run: | + hooks="$HOME/.xlcore/dalamud/Hooks/dev" + mkdir -p "$hooks" + curl -fsSL https://goatcorp.github.io/dalamud-distrib/stg/latest.zip -o dalamud.zip + unzip -oq dalamud.zip -d "$hooks" + + - name: Restore + run: dotnet restore Craftimizer/Craftimizer.csproj + + - name: Build (Release) + run: dotnet build Craftimizer/Craftimizer.csproj --configuration Release --no-restore diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml new file mode 100644 index 0000000..90e26ac --- /dev/null +++ b/.gitea/workflows/release.yml @@ -0,0 +1,139 @@ +name: Release + +# Triggered when a vX.Y.Z tag is pushed. Builds the plugin against the +# current Dalamud staging branch, locates the latest.zip produced by +# DalamudPackager, extracts the matching changelog block from CHANGELOG.md, +# and attaches everything to the Gitea Release. +# +# User-controlled inputs touched by this workflow: +# - the tag name (filtered by on.tags = v*, validated again at runtime +# against ^v\d+\.\d+\.\d+$ before being used in any string) +# All other values are either repo-controlled (paths under +# Craftimizer/bin/x64/Release derived from find) or pinned URLs to goatcorp +# / gitea. Nothing from a webhook event payload (issue/PR titles, commit +# messages, etc.) flows into a run-step. + +on: + push: + tags: + - 'v*' + # Manual recovery trigger. Use Gitea's "Run workflow" UI and select the + # tag (e.g. v0.1.0) from the Ref dropdown - not main. The Validate tag + # ref step below hard-fails if a non-tag ref is selected, because + # release-action reads GITHUB_REF directly and rejects anything that + # does not start with refs/tags/. + workflow_dispatch: + +permissions: + contents: write + +jobs: + release: + name: Build and attach release ZIP + runs-on: ubuntu-latest + timeout-minutes: 20 + + steps: + - name: Validate tag ref + run: | + if [[ "${GITHUB_REF}" != refs/tags/v* ]]; then + echo "::error::Release workflow must run on a v*.X.Y tag ref, got ${GITHUB_REF}" + echo "::error::Push a tag, or pick the tag (not main) in the workflow_dispatch Ref dropdown." + exit 1 + fi + + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Setup .NET 10 + uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5 + with: + dotnet-version: 10.0.x + + - name: Download Dalamud staging + run: | + hooks="$HOME/.xlcore/dalamud/Hooks/dev" + mkdir -p "$hooks" + curl -fsSL https://goatcorp.github.io/dalamud-distrib/stg/latest.zip -o dalamud.zip + unzip -oq dalamud.zip -d "$hooks" + + - name: Build (Release) + run: dotnet build Craftimizer/Craftimizer.csproj --configuration Release + + - name: Locate latest.zip + id: locate + run: | + zip="$(find Craftimizer/bin/x64/Release -name latest.zip -print -quit)" + if [ -z "$zip" ]; then + echo "latest.zip not found under Craftimizer/bin/x64/Release" >&2 + exit 1 + fi + echo "Found: $zip" + echo "path=$zip" >> "$GITHUB_OUTPUT" + + # Extract the changelog block for the tagged version from CHANGELOG.md. + # Convention: each release block starts with `## vX.Y.Z` and ends at + # the next `## v` or the `---` trailer at the bottom. Fails the + # workflow if no block exists for the tagged version, which is the + # automated counterpart to the "csproj + repo.json + CHANGELOG kept + # in sync" rule. + - name: Generate release body + env: + TAG_NAME: ${{ github.ref_name }} + run: | + set -euo pipefail + tag="$TAG_NAME" + if [[ ! "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "::error::Refusing to generate release body for non-semver tag: $tag" + exit 1 + fi + + version="${tag#v}" + changelog="CHANGELOG.md" + + # awk extracts the block starting at "## vX.Y.Z" and stops at the + # next "## v" or the "---" trailer. + block="$(awk -v ver="## v${version}" ' + $0 ~ "^"ver" " { take=1; print; next } + take && /^## v[0-9]+\./ { exit } + take && /^---$/ { exit } + take { print } + ' "$changelog")" + + if [ -z "$block" ]; then + echo "::error::No changelog entry for version $version found in $changelog. Update the changelog before tagging a release." + exit 1 + fi + + footer="" + if [ -f .gitea/release-footer.md ]; then + footer="$(cat .gitea/release-footer.md)" + fi + + { + echo "$block" + echo "" + echo "$footer" + } > release-body.md + + echo "Generated release body for $tag:" + echo "----------------------------------------" + cat release-body.md + echo "----------------------------------------" + + - name: Expose release body for release-action + id: body + shell: bash + run: | + { + echo 'content<> "$GITHUB_OUTPUT" + + - name: Attach to Gitea release + uses: https://gitea.com/actions/release-action@main + with: + files: ${{ steps.locate.outputs.path }} + body: ${{ steps.body.outputs.content }} + api_key: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitea/workflows/security.yml b/.gitea/workflows/security.yml new file mode 100644 index 0000000..c1bcf4b --- /dev/null +++ b/.gitea/workflows/security.yml @@ -0,0 +1,12 @@ +name: Security +on: + push: + branches: [main, master] + pull_request: + schedule: + - cron: '0 6 * * 1' + workflow_dispatch: + +jobs: + scan: + uses: JonKazama-Hellion/security-workflows/.gitea/workflows/security-scan.yml@main diff --git a/.githooks/pre-push b/.githooks/pre-push new file mode 100755 index 0000000..05e280d --- /dev/null +++ b/.githooks/pre-push @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +# .githooks/pre-push — invokes preflight.sh (A=version, B=build, C=csharpier, D=markdownlint). +exec scripts/preflight.sh diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 0000000..5d1a7fb --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,18 @@ +{ + "MD003": { "style": "atx" }, + "MD004": { "style": "dash" }, + "MD007": { "indent": 2 }, + "MD009": { "br_spaces": 2, "strict": false, "list_item_empty_lines": false }, + "MD013": false, + "MD024": { "siblings_only": true }, + "MD029": false, + "MD033": false, + "MD036": false, + "MD040": true, + "MD041": false, + "MD046": { "style": "fenced" }, + "MD048": { "style": "backtick" }, + "MD049": { "style": "underscore" }, + "MD050": { "style": "asterisk" }, + "MD060": false +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a37e1cf --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,39 @@ +# Changelog + +## v0.1.0 — First Hellion fork release (2026-05-26) + +Initial Hellion Forge maintenance release of the Craftimizer fork. Combines +the Dalamud SDK 15 migration, the rebrand to Forgeimizer, and the conflict +detector into one shippable plugin. + +- **Dalamud SDK 14 → 15** for FFXIV 7.5+ compatibility. Migrates the call + sites the SDK 15 compiler refused: `FFXIVClientStructs.FFXIV.Component.GUI.ValueType` + renamed to `AtkValueType`; Dalamud `ImRaii.IEndObject` (removed in SDK 15) + replaced by a local `IEndObject` interface in `ImRaii2.cs`; direct Dalamud + ImRaii returns switched to typed disposables (`ImRaii.TabItemDisposable`, + `ImRaii.ColorDisposable`); `Settings.TabItem` ref-bool lifetime issue fixed + by switching to the `ImRaii.TabItem(label, flags)` overload. +- **Rebrand to Forgeimizer.** Assembly name, plugin manifest, `WindowSystem` + name, the About-tab header, the `MacroMate` default, and a new + `/forgeimizer` slash command alias all use the new name. The plugin + installs into its own `pluginConfigs/Forgeimizer/` slot and shows up + separately in `/xlplugins`. +- **Conflict detector.** Forgeimizer refuses to load if upstream Craftimizer + is active in the same Dalamud instance. Throws an `InvalidOperationException` + with a clear message pointing at `/xlplugins`. Both plugins hook + `UseAction` and `IsActionHighlighted`, so running them in parallel would + corrupt either's state. +- **Hellion Forge custom repo.** Friend-circle distribution via + `https://gitea.hellion-forge.cloud/JonKazama-Hellion/Craftimizer/raw/branch/main/repo.json` + for Dalamud's custom plugin repositories list. + +Internal namespaces (`Craftimizer.*`) intentionally left alone. Crafting +logic, solver, simulator, recipe data layer, synthesis hooks, macro engine, +and all UI windows are unchanged from upstream Craftimizer 2.9.1.1. + +Based on Craftimizer 2.9.1.1 (upstream WorkingRobot/Craftimizer, MIT). + +--- + +Full history: + diff --git a/Craftimizer/Craftimizer.csproj b/Craftimizer/Craftimizer.csproj index a695fd0..378318f 100644 --- a/Craftimizer/Craftimizer.csproj +++ b/Craftimizer/Craftimizer.csproj @@ -1,45 +1,44 @@ - - Asriel Camora (original); Jon Kazama / Hellion Forge (fork) - 2.9.1.1 - Forgeimizer - https://gitea.hellion-forge.cloud/JonKazama-Hellion/Craftimizer - Debug;Release - + + Asriel Camora (original); Jon Kazama / Hellion Forge (fork) + 0.1.0 + Forgeimizer + https://gitea.hellion-forge.cloud/JonKazama-Hellion/Craftimizer + Debug;Release + - - net10.0-windows - x64 - enable - true - false - win-x64 - false - false - true - true - - - - - - - - - - - - + + net10.0-windows + x64 + enable + true + false + win-x64 + false + false + true + true + - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + diff --git a/README.md b/README.md index f826449..44c4398 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # Forgeimizer +[![Build](https://gitea.hellion-forge.cloud/JonKazama-Hellion/Craftimizer/actions/workflows/build.yml/badge.svg?branch=main)](https://gitea.hellion-forge.cloud/JonKazama-Hellion/Craftimizer/actions/workflows/build.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) +[![Latest release](https://img.shields.io/badge/release-v0.1.0-brightgreen)](https://gitea.hellion-forge.cloud/JonKazama-Hellion/Craftimizer/releases/latest) [![Dalamud API](https://img.shields.io/badge/Dalamud-API_15-purple)](https://github.com/goatcorp/Dalamud) [![.NET](https://img.shields.io/badge/.NET-10.0-512BD4)](https://dotnet.microsoft.com/) [![FFXIV](https://img.shields.io/badge/FFXIV-7.5+-c3a37f)](https://www.finalfantasyxiv.com/) @@ -10,12 +12,17 @@ Hellion Forge

-**Version 2.9.1.1** — a Hellion Forge maintenance fork of -[Craftimizer](https://github.com/WorkingRobot/Craftimizer) by +**Version 0.1.0** — a Hellion Forge maintenance fork of +[Craftimizer](https://github.com/WorkingRobot/Craftimizer) 2.9.1.1 by [Asriel Camora](https://github.com/WorkingRobot), brought back to life on Dalamud SDK 15 for FFXIV 7.5+. Installs as a standalone plugin under the name **Forgeimizer**, refuses to load while upstream Craftimizer is active. +Forgeimizer uses an independent version line (starting at 0.1.0). The +upstream Craftimizer version the current fork is built on is always +called out in the description above and the changelog entry for each +release. See [`CHANGELOG.md`](CHANGELOG.md) for the full release history. + Upstream Craftimizer received its last update for FFXIV 7.4 in late 2025 and has been dormant since. With the Dalamud API jump from level 14 to 15 in early 2026, the plugin stopped loading. Forgeimizer is a narrow @@ -117,11 +124,11 @@ All upstream Craftimizer slash commands work unchanged, plus one alias: | Command | What it does | | -------------------------------------- | ------------------------------------------------ | -| `/craftimizer` *or* **`/forgeimizer`** | Open the settings window | -| `/crafteditor` *or* `/macroeditor` | Open the crafting macro editor | +| `/craftimizer` _or_ **`/forgeimizer`** | Open the settings window | +| `/crafteditor` _or_ `/macroeditor` | Open the crafting macro editor | | `/craftaction` | Execute the next suggested action in synth helper | | `/craftretry` | Click "Retry" in the synthesis helper | -| `/craftmacros` *or* `/macrolist` | Open the crafting macros window | +| `/craftmacros` _or_ `/macrolist` | Open the crafting macros window | --- diff --git a/dotnet-tools.json b/dotnet-tools.json new file mode 100644 index 0000000..5573da1 --- /dev/null +++ b/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://json.schemastore.org/dotnet-tools.json", + "version": 1, + "isRoot": true, + "tools": { + "csharpier": { + "version": "1.2.6", + "commands": ["csharpier"], + "rollForward": false + } + } +} diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..97d4d16 --- /dev/null +++ b/renovate.json @@ -0,0 +1,49 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended", + ":dependencyDashboard", + ":semanticCommits", + ":timezone(Europe/Berlin)", + "schedule:weekly" + ], + "labels": ["dependencies", "renovate"], + "assignees": ["JonKazama-Hellion"], + "prHourlyLimit": 10, + "prConcurrentLimit": 20, + "rebaseWhen": "behind-base-branch", + "packageRules": [ + { + "description": "Group all minor and patch updates per ecosystem in one PR", + "matchUpdateTypes": ["minor", "patch"], + "groupName": "minor and patch updates ({{manager}})" + }, + { + "description": "Major updates always get their own PR with breaking-change label", + "matchUpdateTypes": ["major"], + "labels": ["dependencies", "major-update", "breaking-change"], + "addLabels": ["needs-review"] + }, + { + "description": "Dev dependencies in their own group", + "matchDepTypes": ["devDependencies"], + "groupName": "dev dependencies" + }, + { + "description": "Pin GitHub Action versions by SHA for supply-chain hygiene", + "matchManagers": ["github-actions"], + "pinDigests": true, + "ignorePaths": [".gitea/workflows/**"] + } + ], + "vulnerabilityAlerts": { + "labels": ["security", "vulnerability"], + "schedule": ["at any time"] + }, + "lockFileMaintenance": { + "enabled": true, + "schedule": ["before 6am on monday"], + "commitMessageAction": "Refresh" + }, + "osvVulnerabilityAlerts": true +} diff --git a/repo.json b/repo.json new file mode 100644 index 0000000..9c4cf99 --- /dev/null +++ b/repo.json @@ -0,0 +1,49 @@ +[ + { + "Author": "Asriel Camora (original); Jon Kazama / Hellion Forge (fork)", + "Name": "Forgeimizer", + "InternalName": "Forgeimizer", + "AssemblyVersion": "0.1.0.0", + "Description": "A Hellion Forge plugin — maintenance fork of Craftimizer by Asriel Camora, brought back to life on Dalamud SDK 15 for FFXIV 7.5+.\n\nSimulate crafts, create computer-assisted macros, and get mid-craft suggestions from the comfort of your own game. Open your crafting log to get started.\n\nAll features come from upstream Craftimizer, unchanged in this fork:\n- Crafting simulator with full action state tracking\n- Macro solver (Raphael Rust crate, bundled)\n- Recipe note overlay\n- Synthesis helper window\n- Macro editor and macro list\n\nThis fork:\n- Refuses to load if upstream Craftimizer is active (no parallel hooks)\n- No features added, no design changes vs upstream\n- Will archive if Asriel ships an official SDK 15 update upstream\n\nBased on Craftimizer 2.9.1.1 (upstream WorkingRobot/Craftimizer, MIT).\nSource: https://gitea.hellion-forge.cloud/JonKazama-Hellion/Craftimizer", + "ApplicableVersion": "any", + "RepoUrl": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/Craftimizer", + "Tags": [ + "crafting", + "doh", + "craft", + "macro", + "solver", + "generator", + "generate", + "simulate", + "sim", + "simulator", + "hellion", + "forge" + ], + "DalamudApiLevel": 15, + "LoadRequiredState": 0, + "LoadSync": false, + "CanUnloadAsync": false, + "LoadPriority": 0, + "Punchline": "A Hellion Forge plugin. Crafting simulator and macro solver for FFXIV, maintenance fork of Asriel Camora's Craftimizer kept current for Dalamud API 15+.", + "Changelog": "**v0.1.0 — First Hellion fork release (2026-05-26)**\n\nInitial Hellion Forge maintenance release of the Craftimizer fork. Combines the Dalamud SDK 15 migration, the rebrand to Forgeimizer, and the conflict detector into one shippable plugin.\n\n- Dalamud SDK 14 → 15 for FFXIV 7.5+ compatibility. Migrates the call sites the SDK 15 compiler refused (ValueType → AtkValueType, local IEndObject interface, typed ImRaii disposables, ImRaii.TabItem ref-bool lifetime fix).\n- Rebrand to Forgeimizer. Assembly name, plugin manifest, WindowSystem name, About-tab header, MacroMate default, and a new /forgeimizer slash command alias all use the new name. The plugin installs into its own pluginConfigs/Forgeimizer/ slot.\n- Conflict detector. Forgeimizer refuses to load if upstream Craftimizer is active in the same Dalamud instance.\n- Hellion Forge custom repo distribution.\n\nInternal namespaces (Craftimizer.*) intentionally left alone. Crafting logic, solver, simulator, recipe data layer, synthesis hooks, macro engine, and all UI windows are unchanged from upstream Craftimizer 2.9.1.1.\n\nBased on Craftimizer 2.9.1.1 (upstream WorkingRobot/Craftimizer, MIT).", + "AcceptsFeedback": true, + "DownloadLinkInstall": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/Craftimizer/releases/download/v0.1.0/latest.zip", + "DownloadLinkUpdate": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/Craftimizer/releases/download/v0.1.0/latest.zip", + "DownloadLinkTesting": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/Craftimizer/releases/download/v0.1.0/latest.zip", + "TestingAssemblyVersion": "0.1.0.0", + "IconUrl": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/Craftimizer/raw/branch/main/icon.png", + "ImageUrls": [ + "https://gitea.hellion-forge.cloud/JonKazama-Hellion/Craftimizer/raw/branch/main/Images/RecipeNote.png", + "https://gitea.hellion-forge.cloud/JonKazama-Hellion/Craftimizer/raw/branch/main/Images/SynthHelper.png", + "https://gitea.hellion-forge.cloud/JonKazama-Hellion/Craftimizer/raw/branch/main/Images/MacroEditor.png" + ], + "DownloadCount": 0, + "IsHide": false, + "IsTestingExclusive": false, + "CategoryTags": [ + "jobs" + ] + } +] diff --git a/scripts/preflight.sh b/scripts/preflight.sh new file mode 100755 index 0000000..c1f8dcf --- /dev/null +++ b/scripts/preflight.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# preflight.sh — pre-push gate. Block A verifies that csproj and repo.json +# agree on the version and tag URLs. Block B does a headless `dotnet build` +# to catch compile-time API drift. Block C runs `dotnet csharpier check` +# against Craftimizer/. Block D runs markdownlint against repo *.md files. + +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 — plugin compile health" +dotnet build Craftimizer/Craftimizer.csproj --configuration Release --nologo --verbosity quiet + +echo "==> preflight: Block C — csharpier reflow check" +dotnet csharpier check Craftimizer/ + +echo "==> preflight: Block D — markdownlint" +# npx --yes avoids a global install; first run caches into ~/.npm/_npx/. +# Subsequent runs are sub-second. +npx --yes markdownlint-cli2 "**/*.md" "#node_modules" "#bin" "#obj" "#.claude" "#CLAUDE.md" + +echo "==> preflight: ALL GREEN" diff --git a/scripts/setup-hooks.sh b/scripts/setup-hooks.sh new file mode 100755 index 0000000..2a4185c --- /dev/null +++ b/scripts/setup-hooks.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +# setup-hooks.sh — installs pre-push hook via core.hooksPath. Idempotent. +# Note: NO pre-commit hook — Forgeimizer is a small maintenance fork with +# no separate test harness; version/manifest/build/lint 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 +chmod +x scripts/preflight.sh +chmod +x scripts/verify-version-consistency.sh +echo "setup-hooks: core.hooksPath set to .githooks. pre-push live." diff --git a/scripts/verify-version-consistency.sh b/scripts/verify-version-consistency.sh new file mode 100755 index 0000000..b7d8120 --- /dev/null +++ b/scripts/verify-version-consistency.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +# verify-version-consistency.sh — Block A of preflight. +# csproj is 3-digit SemVer; repo.json AssemblyVersion is 4-digit (.0 suffix). + +set -euo pipefail +ROOT="$(cd "$(dirname "$0")/.." && pwd)" + +CSPROJ="$ROOT/Craftimizer/Craftimizer.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 '[^<]+' "$CSPROJ" | head -1 | sed -E 's/<[^>]+>//g')" +[ -n "$CSPROJ_VER" ] || fail "$CSPROJ has no 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"