Skip to content

feat(npc): declare prefab roles through NPC properties #954

feat(npc): declare prefab roles through NPC properties

feat(npc): declare prefab roles through NPC properties #954

Workflow file for this run

name: Build and Deploy Documentation
on:
push:
branches-ignore:
- 'agent/**'
- 'automation/**'
- 'dependabot/**'
- 'feat/**'
- 'fix/**'
- 'hotfix/**'
- 'release/**'
pull_request:
workflow_dispatch:
inputs:
assembly_branch:
description: 'Assembly branch to use (main or beta)'
required: false
default: 'main'
type: choice
options:
- main
- beta
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: ${{ github.event_name == 'pull_request' && format('docs-pr-{0}', github.event.pull_request.number) || 'pages' }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
documentation:
name: documentation
runs-on: ubuntu-latest
steps:
- name: Checkout S1API
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
9.0.x
10.0.x
# Determine which assembly branch to use (beta or main) - MUST run before cache
- name: Determine Assembly Branch
id: assembly-branch
run: |
# Manual workflow dispatch takes precedence
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]] && [[ -n "${{ inputs.assembly_branch }}" ]]; then
echo "branch=${{ inputs.assembly_branch }}" >> $GITHUB_OUTPUT
echo "Using ${{ inputs.assembly_branch }} assemblies (manual trigger)"
elif [[ "${{ github.ref_name }}" == "beta" ]] || [[ "${{ github.base_ref }}" == "beta" ]]; then
echo "branch=beta" >> $GITHUB_OUTPUT
echo "Using beta assemblies for beta branch workflow"
elif [[ "${{ github.event_name }}" == "pull_request" ]] && [[ "${{ contains(github.event.pull_request.labels.*.name, 'beta') }}" == "true" ]]; then
echo "branch=beta" >> $GITHUB_OUTPUT
echo "Using beta assemblies for PR with beta label"
else
echo "branch=main" >> $GITHUB_OUTPUT
echo "Using main/stable assemblies"
fi
- name: Create Assembly Directory Structure
run: |
mkdir -p S1API/ScheduleOneAssemblies/Managed
mkdir -p S1API/ScheduleOneAssemblies/MelonLoader
# Try to restore assemblies from cache first (for external PRs)
# Cache key includes assembly branch to separate beta from main
# v4 includes Unity.Burst.dll, which the Mono test host needs while
# reflecting native test data.
# Only use cache for PR events to avoid stale assemblies after merges
- name: Restore Game Assemblies from Cache
id: cache-assemblies
if: github.event_name == 'pull_request'
uses: actions/cache/restore@v4
with:
path: S1API/ScheduleOneAssemblies
key: game-assemblies-v4-${{ steps.assembly-branch.outputs.branch }}-${{ hashFiles('S1API/S1API.csproj') }}
restore-keys: |
game-assemblies-v4-${{ steps.assembly-branch.outputs.branch }}-
# Only checkout game assemblies if cache miss AND we have access to secrets
- name: Checkout Game Assemblies
uses: actions/checkout@v4
with:
repository: ${{ secrets.GAME_ASSEMBLIES_REPO }}
token: ${{ secrets.GAME_ASSEMBLIES_TOKEN }}
ref: ${{ steps.assembly-branch.outputs.branch }}
path: game-assemblies
fetch-depth: 1
if: steps.cache-assemblies.outputs.cache-hit != 'true' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
- name: Debug Game Assemblies Structure
run: |
echo "=== Game Assemblies Repository Structure ==="
find game-assemblies -type f -name "*.dll" | head -20
echo ""
echo "=== Looking for Assembly-CSharp.dll ==="
find game-assemblies -name "Assembly-CSharp.dll" -type f
if: steps.cache-assemblies.outputs.cache-hit != 'true' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
- name: Copy Game Assemblies
run: |
# Copy Mono assemblies
if [ -d "game-assemblies/Managed" ]; then
cp -r game-assemblies/Managed/* S1API/ScheduleOneAssemblies/Managed/ || echo "Failed to copy Mono assemblies"
echo "Copied Mono assemblies from game-assemblies/Managed/"
else
echo "No game-assemblies/Managed directory found"
fi
# Copy mod loader assemblies
if [ -d "game-assemblies/MelonLoader" ]; then
cp -r game-assemblies/MelonLoader/* S1API/ScheduleOneAssemblies/MelonLoader/ || echo "Failed to copy MelonLoader assemblies"
echo "Copied MelonLoader assemblies"
else
echo "No game-assemblies/MelonLoader directory found"
fi
# Ensure Unity.TextMeshPro.dll is available
if [ ! -f "S1API/ScheduleOneAssemblies/Managed/Unity.TextMeshPro.dll" ]; then
echo "Unity.TextMeshPro.dll not found in Managed folder, searching for it..."
TMPRO_PATH=$(find game-assemblies -name "Unity.TextMeshPro.dll" -type f | head -1)
if [ -n "$TMPRO_PATH" ]; then
echo "Found Unity.TextMeshPro.dll at: $TMPRO_PATH"
cp "$TMPRO_PATH" S1API/ScheduleOneAssemblies/Managed/ || echo "Failed to copy Unity.TextMeshPro.dll"
echo "Copied Unity.TextMeshPro.dll to Managed folder"
else
echo "Warning: Unity.TextMeshPro.dll not found in game-assemblies repository"
echo "This may cause build failures for files that use TMPro"
fi
fi
if: steps.cache-assemblies.outputs.cache-hit != 'true' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
- name: Verify Assembly Copies
run: |
echo "=== Verifying Assembly Structure ==="
ls -la S1API/ScheduleOneAssemblies/
echo ""
echo "=== Managed Assemblies ==="
ls -la S1API/ScheduleOneAssemblies/Managed/ | head -10
echo ""
echo "=== MelonLoader Assemblies ==="
ls -la S1API/ScheduleOneAssemblies/MelonLoader/ | head -10
echo ""
echo "=== Critical Assembly Check ==="
if [ -f S1API/ScheduleOneAssemblies/Managed/Assembly-CSharp.dll ]; then
echo "✓ Found Assembly-CSharp.dll"
ls -l S1API/ScheduleOneAssemblies/Managed/Assembly-CSharp.dll
else
echo "✗ Missing Assembly-CSharp.dll"
if [ -d "game-assemblies" ]; then
echo "Attempting auto-discovery..."
find game-assemblies -name "Assembly-CSharp.dll" -type f -exec ls -l {} \;
fi
fi
echo ""
echo "=== TextMeshPro Assembly Check ==="
if [ -f S1API/ScheduleOneAssemblies/Managed/Unity.TextMeshPro.dll ]; then
echo "✓ Found Unity.TextMeshPro.dll"
ls -l S1API/ScheduleOneAssemblies/Managed/Unity.TextMeshPro.dll
else
echo "✗ Missing Unity.TextMeshPro.dll"
if [ -d "game-assemblies" ]; then
echo "Attempting auto-discovery..."
find game-assemblies -name "Unity.TextMeshPro.dll" -type f -exec ls -l {} \;
echo "Searching for TextMeshPro assemblies..."
find game-assemblies -name "*TextMeshPro*" -type f -exec ls -l {} \;
fi
fi
echo ""
echo "=== Burst Assembly Check ==="
if [ -f S1API/ScheduleOneAssemblies/Managed/Unity.Burst.dll ]; then
echo "✓ Found Unity.Burst.dll"
ls -l S1API/ScheduleOneAssemblies/Managed/Unity.Burst.dll
else
echo "✗ Missing Unity.Burst.dll"
exit 1
fi
echo ""
echo "=== MelonLoader Assembly Check ==="
if [ -f S1API/ScheduleOneAssemblies/MelonLoader/0Harmony.dll ]; then
echo "✓ Found 0Harmony.dll"
ls -l S1API/ScheduleOneAssemblies/MelonLoader/0Harmony.dll
else
echo "✗ Missing 0Harmony.dll"
if [ -d "game-assemblies" ]; then
echo "Looking for MelonLoader assemblies..."
find game-assemblies -name "0Harmony.dll" -type f -exec ls -l {} \;
fi
fi
echo ""
if [ "${{ steps.cache-assemblies.outputs.cache-hit }}" == "true" ]; then
echo "✓ Using cached assemblies for build"
else
echo "✓ Using fresh assemblies from repository"
fi
# Save only verified non-empty assembly caches.
- name: Save Game Assemblies to Cache
uses: actions/cache/save@v4
if: always() && steps.cache-assemblies.outputs.cache-hit != 'true' && hashFiles('S1API/ScheduleOneAssemblies/Managed/Assembly-CSharp.dll') != '' && hashFiles('S1API/ScheduleOneAssemblies/Managed/Unity.Burst.dll') != '' && hashFiles('S1API/ScheduleOneAssemblies/MelonLoader/0Harmony.dll') != '' && ((github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/beta' || github.ref == 'refs/heads/npc-prefabs')))
with:
path: S1API/ScheduleOneAssemblies
key: game-assemblies-v4-${{ steps.assembly-branch.outputs.branch }}-${{ hashFiles('S1API/S1API.csproj') }}
- name: Create CI Build Properties
run: |
cat > ci.build.props << 'EOF'
<Project>
<PropertyGroup>
<AutomateLocalDeployment>false</AutomateLocalDeployment>
<!-- CI Assembly Paths (relative to S1API/S1API.csproj) -->
<MelonLoaderAssembliesPath>$(MSBuildThisFileDirectory)S1API/ScheduleOneAssemblies/MelonLoader</MelonLoaderAssembliesPath>
<MelonLoaderMonoAssembliesPath>$(MSBuildThisFileDirectory)S1API/ScheduleOneAssemblies/MelonLoader</MelonLoaderMonoAssembliesPath>
<!-- Mono Configuration -->
<LocalMonoDeploymentPath>null</LocalMonoDeploymentPath>
<MonoAssembliesPath>$(MSBuildThisFileDirectory)S1API/ScheduleOneAssemblies/Managed</MonoAssembliesPath>
</PropertyGroup>
</Project>
EOF
- name: Verify Build Properties
run: |
echo "=== CI Build Properties ==="
cat ci.build.props
echo ""
echo "=== MSBuild Property Resolution Test ==="
cd S1API
echo "Testing MonoAssembliesPath resolution (expect ./ScheduleOneAssemblies from project dir)..."
if [ -f "./ScheduleOneAssemblies/Managed/Assembly-CSharp.dll" ]; then
echo "✓ Assembly-CSharp.dll accessible from build context"
ls -l ./ScheduleOneAssemblies/Managed/Assembly-CSharp.dll
else
echo "✗ Assembly-CSharp.dll NOT accessible from build context"
echo "Current directory: $(pwd)"
echo "Looking for: ./ScheduleOneAssemblies/Managed/Assembly-CSharp.dll"
ls -la ./ScheduleOneAssemblies/Managed/ || echo "Directory doesn't exist"
fi
echo ""
echo "Testing MelonLoaderAssembliesPath resolution..."
if [ -f "./ScheduleOneAssemblies/MelonLoader/0Harmony.dll" ]; then
echo "✓ 0Harmony.dll accessible from build context"
ls -l ./ScheduleOneAssemblies/MelonLoader/0Harmony.dll
else
echo "✗ 0Harmony.dll NOT accessible from build context"
echo "Current directory: $(pwd)"
echo "Looking for: ./ScheduleOneAssemblies/MelonLoader/0Harmony.dll"
ls -la ./ScheduleOneAssemblies/MelonLoader/ || echo "Directory doesn't exist"
fi
- name: Restore dependencies
run: dotnet restore S1API.sln -p:Configuration=MonoMelon
- name: Build S1API
run: dotnet build S1API/S1API.csproj --no-restore -c MonoMelon -v normal
- name: Run Mono contract and compatibility tests
run: >-
dotnet test
S1API.Tests/S1API.Tests.csproj
--no-restore
--configuration MonoMelon
--verbosity normal
--property:AutomateLocalDeployment=false
- name: Upload S1API Mono Artifact
uses: actions/upload-artifact@v4
with:
name: S1API-Mono
path: S1API/bin/MonoMelon/netstandard2.1/S1API.dll
- name: Install DocFX
run: dotnet tool install -g docfx
- name: Build documentation
run: |
cd S1API
docfx docfx.json
- name: Check public API documentation coverage
run: >-
dotnet run
--project tools/S1APIDocumentationCoverage/S1APIDocumentationCoverage.csproj
--configuration Release
--
--api-directory S1API/api
--minimum 80
- name: Preserve pull request API assembly
if: github.event_name == 'pull_request'
run: |
mkdir -p "$RUNNER_TEMP/s1api-api-compat"
cp \
S1API/bin/MonoMelon/netstandard2.1/S1API.dll \
"$RUNNER_TEMP/s1api-api-compat/current.dll"
- name: Build target branch API baseline
if: github.event_name == 'pull_request'
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
git checkout --detach --force "$BASE_SHA"
baseline_tag="$(git describe --tags --abbrev=0 --match 'v[0-9]*' "$BASE_SHA" 2>/dev/null || true)"
if [[ -n "$baseline_tag" ]] && git diff --quiet "$baseline_tag" "$BASE_SHA" -- \
':(glob)S1API/**/*.cs' \
S1API/S1API.csproj \
S1API/Directory.Build.props \
Directory.Build.props; then
baseline_version="${baseline_tag#v}"
release_dir="$RUNNER_TEMP/s1api-api-compat/release"
release_zip="$release_dir/S1API-Forked-${baseline_version}.zip"
mkdir -p "$release_dir"
gh release download "$baseline_tag" \
--repo "$GITHUB_REPOSITORY" \
--pattern "S1API-Forked-${baseline_version}.zip" \
--dir "$release_dir"
unzip -p "$release_zip" \
Mods/S1API.Mono.MelonLoader.dll \
> "$RUNNER_TEMP/s1api-api-compat/baseline.dll"
echo "Using shipped ${baseline_tag} Mono assembly as the API baseline"
else
dotnet restore S1API/S1API.csproj -p:Configuration=MonoMelon
dotnet build \
S1API/S1API.csproj \
--no-restore \
--configuration MonoMelon \
--verbosity minimal \
--property:AutomateLocalDeployment=false
cp \
S1API/bin/MonoMelon/netstandard2.1/S1API.dll \
"$RUNNER_TEMP/s1api-api-compat/baseline.dll"
echo "Using target-branch source build as the API baseline"
fi
test -s "$RUNNER_TEMP/s1api-api-compat/baseline.dll"
git checkout --detach --force "$GITHUB_SHA"
- name: Restore ApiCompat tool cache
if: github.event_name == 'pull_request'
uses: actions/cache@v4
with:
path: ${{ runner.temp }}/apicompat
key: apicompat-${{ runner.os }}-${{ runner.arch }}-10.0.302
- name: Install ApiCompat
if: github.event_name == 'pull_request'
run: |
if [ ! -x "$RUNNER_TEMP/apicompat/apicompat" ]; then
dotnet tool install \
Microsoft.DotNet.ApiCompat.Tool \
--tool-path "$RUNNER_TEMP/apicompat" \
--version 10.0.302
fi
- name: Check public API compatibility
if: github.event_name == 'pull_request'
run: |
"$RUNNER_TEMP/apicompat/apicompat" \
--left "$RUNNER_TEMP/s1api-api-compat/baseline.dll" \
--right "$RUNNER_TEMP/s1api-api-compat/current.dll" \
--suppression-file .github/api-compat-suppressions.xml \
--enable-rule-cannot-change-parameter-name \
--enable-rule-attributes-must-match
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./S1API/_site
if: github.event_name != 'pull_request' && github.ref != 'refs/heads/beta'
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: documentation
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/stable' || github.ref == 'refs/heads/npc-prefabs'
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4