Initial template setup
CI / build (push) Failing after 34s

This commit is contained in:
2026-05-09 16:41:15 +02:00
commit 4a2e840888
20 changed files with 561 additions and 0 deletions
+30
View File
@@ -0,0 +1,30 @@
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 4
[*.{json,yaml,yml,md}]
indent_size = 2
[*.{csproj,xml}]
indent_size = 2
[*.cs]
# Namespace conventions
csharp_style_namespace_declarations = file_scoped:warning
# Newline preferences
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
# Var preferences
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
# Expression-bodied members
csharp_style_expression_bodied_methods = when_on_single_line:silent
csharp_style_expression_bodied_properties = true:suggestion
+26
View File
@@ -0,0 +1,26 @@
---
name: Bug Report
about: Something is broken or behaves unexpectedly
title: "[Bug] "
labels: ["bug"]
---
## What happened
<!-- Describe the bug. What did you do, what did you expect, what happened instead? -->
## Steps to reproduce
1.
2.
3.
## Environment
- Version:
- OS:
- Anything else relevant:
## Logs / Screenshots
<!-- Paste relevant log output or attach screenshots. Use ```code blocks``` for logs. -->
+22
View File
@@ -0,0 +1,22 @@
---
name: Feature Request
about: Suggest an idea or improvement
title: "[Feature] "
labels: ["enhancement"]
---
## The problem
<!-- What are you trying to do? What's missing or annoying? -->
## Proposed solution
<!-- How would you solve it? Concrete behavior, not implementation details. -->
## Alternatives considered
<!-- What else did you think about? Why didn't that work? -->
## Additional context
<!-- Screenshots, examples from other tools, related issues. -->
+22
View File
@@ -0,0 +1,22 @@
## Summary
<!-- What does this PR do? 1-3 bullet points. -->
-
## Why
<!-- Linked issue, motivation, or context. "Fixes #N" if applicable. -->
## Testing
<!-- How did you verify this works? -->
- [ ]
## Checklist
- [ ] Code builds without warnings
- [ ] Tests pass (or N/A)
- [ ] Documentation updated (or N/A)
- [ ] No secrets or credentials committed
+16
View File
@@ -0,0 +1,16 @@
---
title: "PluginNameTemplate v0.1.0"
subtitle: "Initial release — short one-liner about what this version brings"
versionsnatur: "Initial release"
---
First public release on the Hellion Forge.
**Highlights**
- Bullet 1
- Bullet 2
**Notes**
Anything testers should know — known issues, breaking changes from the previous version, etc. Keep it tight; the embed has a ~5500 char total cap.
+27
View File
@@ -0,0 +1,27 @@
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'
- name: Restore
run: dotnet restore
- name: Build (Release)
run: dotnet build -c Release --no-restore
- name: Verify Dalamud manifest exists
run: test -f bin/Release/PluginNameTemplate/PluginNameTemplate.json
+75
View File
@@ -0,0 +1,75 @@
# Forge-Auto-Announce
# Triggers on tag push (v*). Reads .gitea/forge-posts/v<X.Y.Z>.md from the
# *tagged tree* (not main), assembles a Discord embed, posts via webhook.
#
# Required repo secret: FORGE_DISCORD_WEBHOOK_URL
# Optional repo secret: FORGE_DISCORD_THREAD_ID (post into a forum thread)
#
# Char-cap reminder: Discord embed total (title + description + footer +
# field values combined) ~5500. Keep .gitea/forge-posts/v*.md trim.
name: Forge Auto-Announce
on:
push:
tags:
- 'v*'
jobs:
announce:
runs-on: ubuntu-latest
steps:
- name: Checkout tagged tree
uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
- name: Compose embed
id: embed
env:
TAG: ${{ github.ref_name }}
run: |
POST_FILE=".gitea/forge-posts/${TAG}.md"
if [ ! -f "$POST_FILE" ]; then
echo "No forge-post file for ${TAG} (looked at ${POST_FILE}). Skipping."
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
TITLE=$(awk -F': ' '/^title:/ {print $2; exit}' "$POST_FILE" | sed 's/^"//;s/"$//')
SUBTITLE=$(awk -F': ' '/^subtitle:/ {print $2; exit}' "$POST_FILE" | sed 's/^"//;s/"$//')
BODY=$(awk '/^---$/{c++;next} c==2' "$POST_FILE")
jq -n \
--arg title "${TITLE:-Release ${TAG}}" \
--arg subtitle "${SUBTITLE}" \
--arg body "$BODY" \
--arg tag "$TAG" \
--arg repo "${{ github.repository }}" \
'{
embeds: [{
title: $title,
description: ($subtitle + "\n\n" + $body),
color: 12731404,
url: ("https://gitea.hellion-forge.cloud/" + $repo + "/releases/tag/" + $tag),
footer: { text: ($repo + " — " + $tag) }
}]
}' > payload.json
echo "skip=false" >> "$GITHUB_OUTPUT"
- name: Post to Discord
if: steps.embed.outputs.skip != 'true'
env:
WEBHOOK: ${{ secrets.FORGE_DISCORD_WEBHOOK_URL }}
THREAD_ID: ${{ secrets.FORGE_DISCORD_THREAD_ID }}
run: |
if [ -z "$WEBHOOK" ]; then
echo "::error::FORGE_DISCORD_WEBHOOK_URL secret missing"
exit 1
fi
URL="$WEBHOOK"
if [ -n "$THREAD_ID" ]; then
URL="${URL}?thread_id=${THREAD_ID}"
fi
curl -fsS -X POST -H 'Content-Type: application/json' --data @payload.json "$URL"
+34
View File
@@ -0,0 +1,34 @@
# Build output
bin/
obj/
out/
*.user
*.suo
*.userosscache
*.sln.docstates
# Visual Studio
.vs/
*.dotsettings.user
# Rider
.idea/
# Test artifacts
[Tt]estResults/
*.coverage
*.coveragexml
# Dalamud packaging
*.pdb
latest.zip
# Local secrets
*.local.json
appsettings.local.json
.env
.env.local
# OS
.DS_Store
Thumbs.db
+7
View File
@@ -0,0 +1,7 @@
# Changelog
Slim copy — only the latest 2-4 versions live here. Older entries move to `docs/CHANGELOG.md`.
## v0.1.0
- Initial release.
+8
View File
@@ -0,0 +1,8 @@
# CODEOWNERS — automatic review-assignment for PRs.
# Syntax: <pattern> <user-or-team-handle>
#
# More: https://docs.gitea.com/usage/code-owners
#
# Default owner for everything in the repo.
# Replace with the appropriate user/team for the new repo.
* @JonKazama-Hellion
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Florian Wathling / Hellion Online Media
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+31
View File
@@ -0,0 +1,31 @@
<Project Sdk="Dalamud.NET.Sdk/13.0.0">
<!--
Replace `Dalamud.NET.Sdk/13.0.0` with the SDK version current at the time
you start your plugin. Check https://github.com/goatcorp/Dalamud.NET.Sdk
for the latest tag.
-->
<PropertyGroup>
<Version>0.1.0</Version>
<AssemblyVersion>0.1.0</AssemblyVersion>
<FileVersion>0.1.0</FileVersion>
<Description>Short one-line description of what your plugin does.</Description>
<Authors>Florian Wathling / Hellion Online Media</Authors>
<Company>Hellion Online Media</Company>
<Copyright>Copyright (c) 2026 Florian Wathling / Hellion Online Media</Copyright>
<RootNamespace>PluginNameTemplate</RootNamespace>
<AssemblyName>PluginNameTemplate</AssemblyName>
</PropertyGroup>
<ItemGroup>
<!--
Add additional NuGet refs here as needed. Dalamud.NET.Sdk pulls in
Dalamud, ImGui.NET, FFXIVClientStructs, Lumina, etc. by default.
-->
</ItemGroup>
<ItemGroup>
<None Include="images\icon.png" Pack="true" PackagePath="\" />
</ItemGroup>
</Project>
+30
View File
@@ -0,0 +1,30 @@
# Dalamud manifest. Used by the official plugin repo and by custom repos
# (see repo.json). Keep in sync with .csproj and repo.json on every version bump.
name: PluginNameTemplate
author: Florian Wathling
punchline: Short tag-line that shows in the plugin installer.
description: |
Longer description of what your plugin does. Markdown is supported in some
Dalamud versions; safest to keep it plain text. Multi-line is fine.
repo_url: https://gitea.hellion-forge.cloud/Hellion-Forge/PluginNameTemplate
icon_url: https://gitea.hellion-forge.cloud/Hellion-Forge/PluginNameTemplate/raw/branch/main/images/icon.png
assembly_version: 0.1.0
testing_assembly_version: 0.1.0
dalamud_api_level: 13
categories:
- utility
tags:
- hellion-forge
changelog: |
v0.1.0
- Initial release.
# Don't override DalamudPackager defaults (Dalamud.NET.Sdk 13+ handles icon and
# image_urls automatically via the .csproj <None Include="images\icon.png" />).
+103
View File
@@ -0,0 +1,103 @@
# Dalamud Plugin Template
A starting point for FFXIV/Dalamud plugins on the [Hellion Forge](https://gitea.hellion-forge.cloud/).
Distilled from the [HellionChat](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat) plugin patterns: csproj layout, configuration handling, window scaffolding, custom-repo manifest, Forge-Auto-Announce workflow, and the version-bump checklist.
---
## How to use this template
1. Click **"Use this template"** at the top of the repository page on the Forge.
2. Pick a name like `MyPlugin` and clone your new repo locally.
3. Find-and-replace `PluginNameTemplate` everywhere with your plugin's name (case-sensitive).
```bash
git ls-files | xargs sed -i 's/PluginNameTemplate/MyPlugin/g'
git mv PluginNameTemplate.csproj MyPlugin.csproj
git mv PluginNameTemplate.yaml MyPlugin.yaml
```
4. Replace `images/icon.png` with your plugin icon (512x512 PNG, transparent background).
5. Update `repo.json` with your real `DownloadLinkInstall` URLs once your CI publishes releases.
6. Implement your plugin in `src/Plugin.cs` and friends.
After the rename, this README should be replaced with your plugin's actual README — the template-usage notes don't belong in your shipped plugin.
---
## Project structure
```
.
├── .editorconfig Code style
├── .gitea/
│ ├── ISSUE_TEMPLATE/ Standard issue forms
│ ├── PULL_REQUEST_TEMPLATE.md
│ └── workflows/
│ ├── ci.yml Build verification on push/PR
│ └── forge-auto-announce.yml Discord announcement on tag
├── docs/CHANGELOG.md Long-form changelog (slim main copy)
├── images/icon.png Plugin icon (replace before shipping)
├── src/
│ ├── Plugin.cs IDalamudPlugin entry point
│ ├── PluginConfiguration.cs IPluginConfiguration
│ └── Windows/
│ └── ConfigWindow.cs Skeleton config window
├── PluginNameTemplate.csproj Dalamud-SDK csproj
├── PluginNameTemplate.yaml Dalamud manifest
├── repo.json Custom-repo manifest for testers
├── CHANGELOG.md Slim changelog (latest 2-4 versions)
├── CODEOWNERS Default reviewer
├── LICENSE MIT
└── README.md This file (replace before shipping)
```
---
## Build
```bash
dotnet restore
dotnet build -c Release
```
DalamudPackager produces the `.zip` artifact under `bin/Release/<PluginName>/latest.zip`.
**Note:** Don't override the default `DalamudPackager.targets` from your csproj — it'll silently strip the icon and ImageUrls from your manifest. If you need custom packaging, do it via csproj properties only.
---
## Versioning
Synchronized version fields (bump all at once):
- `<PluginName>.csproj` → `AssemblyVersion`
- `<PluginName>.yaml` → `assembly_version` + `changelog`
- `repo.json` → `AssemblyVersion`, `TestingAssemblyVersion`, all 3 `DownloadLink*` URLs, `Description`, `Changelog`
- `CHANGELOG.md` (slim) and `docs/CHANGELOG.md` (full) — keep the latest 2-4 versions in the slim copy
- `.gitea/forge-posts/v<X.Y.Z>.md` — Discord announcement payload
Sanity-check before tagging:
```bash
grep -rn "<old-version>" . | grep -v -E '(bin|obj|node_modules|.git/)'
```
The Forge-Auto-Announce workflow reads from the **tagged tree**, not main. If a fix-commit lands after the tag, force-push the tag.
---
## Testing
Service classes coupled to Dalamud (`IPluginInterface`, `IDataManager`, etc.) cannot be instantiated directly in xUnit because the Dalamud assembly isn't on the test AppDomain. Patterns that work:
- **Pure helpers** — extract logic into Dalamud-free classes, test those directly
- **Constructor injection** — pass utility dependencies in, mock them in tests
- **External test repo** — keep tests in a private side-repo if they need a richer harness
The default csproj has no test project. Add one when there's something to test.
---
## License
MIT — see `LICENSE`.
+7
View File
@@ -0,0 +1,7 @@
# Changelog (full)
The complete history. Newest entries at the top. The slim `CHANGELOG.md` in the repo root keeps the latest 2-4 versions; older ones move here.
## v0.1.0
- Initial release.
+3
View File
@@ -0,0 +1,3 @@
Replace this file with `icon.png` (512x512 PNG, transparent background) before shipping.
The plugin manifest (yaml + repo.json) references `images/icon.png` directly.
+20
View File
@@ -0,0 +1,20 @@
[
{
"Author": "Florian Wathling",
"Name": "PluginNameTemplate",
"Punchline": "Short tag-line that shows in the plugin installer.",
"Description": "Longer description for testers using the custom repo.",
"InternalName": "PluginNameTemplate",
"AssemblyVersion": "0.1.0",
"TestingAssemblyVersion": "0.1.0",
"RepoUrl": "https://gitea.hellion-forge.cloud/Hellion-Forge/PluginNameTemplate",
"ApplicableVersion": "any",
"Tags": ["hellion-forge"],
"DalamudApiLevel": 13,
"IconUrl": "https://gitea.hellion-forge.cloud/Hellion-Forge/PluginNameTemplate/raw/branch/main/images/icon.png",
"DownloadLinkInstall": "https://gitea.hellion-forge.cloud/Hellion-Forge/PluginNameTemplate/releases/download/v0.1.0/latest.zip",
"DownloadLinkUpdate": "https://gitea.hellion-forge.cloud/Hellion-Forge/PluginNameTemplate/releases/download/v0.1.0/latest.zip",
"DownloadLinkTesting": "https://gitea.hellion-forge.cloud/Hellion-Forge/PluginNameTemplate/releases/download/v0.1.0/latest.zip",
"Changelog": "v0.1.0 — Initial release."
}
]
+28
View File
@@ -0,0 +1,28 @@
using Dalamud.IoC;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
namespace PluginNameTemplate;
public sealed class Plugin : IDalamudPlugin
{
[PluginService] public static IDalamudPluginInterface Pi { get; private set; } = null!;
[PluginService] public static IPluginLog Log { get; private set; } = null!;
[PluginService] public static ICommandManager Commands { get; private set; } = null!;
private readonly PluginConfiguration config;
public Plugin()
{
this.config = PluginConfiguration.Load();
// Register your commands, hooks, windows, etc. here.
Log.Information("PluginNameTemplate loaded.");
}
public void Dispose()
{
// Unregister anything that was registered above. Order matters —
// dispose UI before hooks, hooks before services.
}
}
+23
View File
@@ -0,0 +1,23 @@
using Dalamud.Configuration;
namespace PluginNameTemplate;
public sealed class PluginConfiguration : IPluginConfiguration
{
public int Version { get; set; } = 1;
// Add your config fields below. Plain types serialize cleanly; complex
// types need [JsonConverter] or a manual migration step.
public bool ExampleToggle { get; set; } = false;
public static PluginConfiguration Load()
{
return Plugin.Pi.GetPluginConfig() as PluginConfiguration ?? new PluginConfiguration();
}
public void Save()
{
Plugin.Pi.SavePluginConfig(this);
}
}
+28
View File
@@ -0,0 +1,28 @@
using System.Numerics;
using Dalamud.Interface.Windowing;
using ImGuiNET;
namespace PluginNameTemplate.Windows;
public sealed class ConfigWindow : Window
{
private readonly PluginConfiguration config;
public ConfigWindow(PluginConfiguration config)
: base("PluginNameTemplate Settings###PluginNameTemplate-config")
{
this.config = config;
this.Size = new Vector2(420, 320);
this.SizeCondition = ImGuiCond.FirstUseEver;
}
public override void Draw()
{
var toggle = this.config.ExampleToggle;
if (ImGui.Checkbox("Example toggle", ref toggle))
{
this.config.ExampleToggle = toggle;
this.config.Save();
}
}
}