Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pr-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,4 @@ jobs:
run: python -m pip install --requirement requirements-docs.txt

- name: Build documentation
run: python -m mkdocs build --strict
run: zensical build --clean --strict
133 changes: 133 additions & 0 deletions .github/workflows/publish-docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
name: Publish documentation

on:
workflow_call:
inputs:
rc_ref:
description: Git ref containing the release-candidate documentation
required: true
type: string

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: starmap-github-pages
cancel-in-progress: false

jobs:
publish:
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/configure-pages@v6

- uses: actions/checkout@v7
with:
ref: ${{ inputs.rc_ref }}
path: rc-source
fetch-depth: 0
fetch-tags: true

- name: Find latest release tag
id: stable
working-directory: rc-source
run: |
stable_tag=$(git tag --list | awk '/^[0-9]+\.[0-9]+\.[0-9]+$/ { print }' | sort -V | tail -n 1)
echo "stable_tag=$stable_tag" >> "$GITHUB_OUTPUT"

- uses: actions/checkout@v7
if: steps.stable.outputs.stable_tag != ''
with:
ref: ${{ steps.stable.outputs.stable_tag }}
path: stable-source
fetch-depth: 1

- uses: actions/setup-python@v6
with:
python-version: "3.12"

- name: Install documentation dependencies
run: python -m pip install --requirement rc-source/requirements-docs.txt

- name: Build latest release documentation
if: steps.stable.outputs.stable_tag != ''
working-directory: stable-source
run: zensical build --clean --strict

- name: Build initial stable documentation
if: steps.stable.outputs.stable_tag == ''
working-directory: rc-source
run: zensical build --clean --strict

- name: Assemble stable documentation
env:
STABLE_TAG: ${{ steps.stable.outputs.stable_tag }}
run: |
mkdir -p "$GITHUB_WORKSPACE/site"
if [[ -n "$STABLE_TAG" ]]; then
cp -a stable-source/site/. "$GITHUB_WORKSPACE/site/"
else
cp -a rc-source/site/. "$GITHUB_WORKSPACE/site/"
fi

- name: Create release-candidate configuration
working-directory: rc-source
run: |
python - <<'PY'
import re
from pathlib import Path

config_path = Path("mkdocs.yml")
config = config_path.read_text(encoding="utf-8")
site_url = re.search(r"(?m)^site_url:\s*(\S+)\s*$", config)
if site_url is None:
raise SystemExit("mkdocs.yml must define site_url")

rc_url = site_url.group(1).rstrip("/") + "/rc/"
config = (
config[:site_url.start()]
+ f"site_url: {rc_url}"
+ config[site_url.end():]
)
site_name = re.search(r"(?m)^site_name:.*$", config)
if site_name is None:
raise SystemExit("mkdocs.yml must define site_name")
config = (
config[:site_name.start()]
+ "site_name: StarMap RC"
+ config[site_name.end():]
)
site_dir = re.search(r"(?m)^site_dir:.*$", config)
if site_dir is None:
config = "site_dir: rc-site\n" + config
else:
config = (
config[:site_dir.start()]
+ "site_dir: rc-site"
+ config[site_dir.end():]
)
Path("mkdocs-rc.yml").write_text(config, encoding="utf-8")
PY

- name: Build release-candidate documentation
working-directory: rc-source
run: zensical build --config-file mkdocs-rc.yml --clean --strict

- name: Add release-candidate documentation
run: |
mkdir -p "$GITHUB_WORKSPACE/site/rc"
cp -a rc-source/rc-site/. "$GITHUB_WORKSPACE/site/rc/"

- name: Upload documentation artifact
uses: actions/upload-pages-artifact@v5
with:
path: site

- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v5
10 changes: 10 additions & 0 deletions .github/workflows/rc-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,13 @@ jobs:
./build_artifacts/version.txt
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

publish-RC-docs:
needs: build
permissions:
contents: read
pages: write
id-token: write
uses: ./.github/workflows/publish-docs.yml
with:
rc_ref: ${{ github.sha }}
44 changes: 6 additions & 38 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ jobs:
prev_version: ${{ steps.version.outputs.prev_version }}
release_tag: ${{ steps.version.outputs.release_tag }}
api_changed: ${{ steps.api_check.outputs.api_changed }}
docs_release: ${{ steps.version.outputs.docs_release }}
steps:
- uses: actions/checkout@v4
with:
Expand Down Expand Up @@ -53,21 +52,12 @@ jobs:
| tail -n 1)
fi

docs_release=false
if [[ -z "$previous_tag" || "${RELEASE_TAG%.*}" != "${previous_tag%.*}" ]]; then
docs_release=true
fi
if ! git ls-remote --exit-code --heads origin gh-pages >/dev/null 2>&1; then
docs_release=true
fi

echo "Release version: $RELEASE_TAG"
echo "Previous release tag: ${previous_tag:-none}"
{
echo "release_tag=$RELEASE_TAG"
echo "new_version=$RELEASE_TAG"
echo "prev_version=$previous_tag"
echo "docs_release=$docs_release"
} >> "$GITHUB_OUTPUT"

- name: Setup NuGet source
Expand Down Expand Up @@ -208,36 +198,14 @@ jobs:
StarMap-${{ needs.build.outputs.new_version }}.zip

publish-docs:
runs-on: ubuntu-latest
needs: [build, release-zip]
if: needs.build.outputs.docs_release == 'true'
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true

- uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install documentation dependencies
run: python -m pip install --requirement requirements-docs.txt

- name: Configure Git identity
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

- name: Publish versioned documentation
env:
RELEASE_VERSION: ${{ needs.build.outputs.new_version }}
run: |
docs_version="${RELEASE_VERSION%.*}"
mike deploy --push --update-aliases "$docs_version" latest
mike set-default --push latest
contents: read
pages: write
id-token: write
uses: ./.github/workflows/publish-docs.yml
with:
rc_ref: main

build-release-installer:
runs-on: windows-latest
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ The [StarMap documentation](https://starmaploader.github.io/StarMap/) covers ins

To publish a release, create and push a semantic-version tag such as `1.2.3` on the commit to release. The release workflow builds the release and installer from that commit; tags can point to commits on any branch.

The documentation site keeps a version for each major/minor release and updates the `latest` version alias.
The documentation site tracks the current release; older releases do not have separate documentation snapshots. The current release-candidate documentation is available at [the RC docs site](https://starmaploader.github.io/StarMap/rc/).

## Installation

Expand Down
9 changes: 9 additions & 0 deletions StarMap.API/Registries/IModServiceRegistry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace StarMap.API.Registries
{
public interface IModServiceRegistry
{
public void RegisterService<T>(T implementation);

public T Get<T>();
}
}
28 changes: 28 additions & 0 deletions StarMap.Core/API/ModServiceRegistry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using StarMap.API.Registries;
using StarMap.Core.ModRepository;
using StarMap.Core.Services;

namespace StarMap.Core.API
{
internal class ModServiceRegistry : IModServiceRegistry
{
private readonly RuntimeMod _mod;
private readonly IModServices _modServices;

public ModServiceRegistry(IModContext modContext, IModServices modServices)
{
_mod = modContext.Mod;
_modServices = modServices;
}

public T Get<T>()
{
return _modServices.Get<T>();
}

public void RegisterService<T>(T implementation)
{
_modServices.Register(_mod, implementation);
}
}
}
55 changes: 55 additions & 0 deletions StarMap.Core/Services/ModServices.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using StarMap.Core.ModRepository;
using System.Collections.Concurrent;
using System.Runtime.Loader;

namespace StarMap.Core.Services
{
internal interface IModServices
{
void Register<T>(RuntimeMod mod, T implementation);
T Get<T>();
}

internal class ModServices : IModServices
{
private readonly ConcurrentDictionary<Type, IModService> _modServices = [];

public void Register<T>(RuntimeMod mod, T implementation)
{
ArgumentNullException.ThrowIfNull(implementation);

var type = typeof(T);
if (AssemblyLoadContext.GetLoadContext(typeof(T).Assembly) != mod.ModAssemblyLoadContext)
throw new InvalidOperationException("The service contract must be defined in the registering mod's assembly load context.");

var modService = new ModService<T>()
{
Implementation = implementation
};

if (!_modServices.TryAdd(type, modService))
throw new InvalidOperationException($"A service for type '{type}' was already registered.");
}

public T Get<T>()
{
if (!_modServices.TryGetValue(typeof(T), out var service))
{
throw new InvalidOperationException($"No service for type '{typeof(T)}' has been registered.");
}

if (service is not ModService<T> modService)
throw new InvalidOperationException($"The registered service for type '{typeof(T)}' has an unexpected type.");

return modService.Implementation;
}

private interface IModService { }

private class ModService<T> : IModService
{
public required T Implementation { get; init; }

}
}
}
3 changes: 3 additions & 0 deletions StarMap.Core/StarMapCore.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.Extensions.DependencyInjection;
using StarMap.API.Registries;
using StarMap.API.Services;
using StarMap.Core.API;
using StarMap.Core.ModRepository;
Expand All @@ -23,11 +24,13 @@ public StarMapCore(AssemblyLoadContext coreAssemblyLoadContext)
serviceCollection.AddSingleton<IStarMapPatches, StarMapPatches>();
serviceCollection.AddSingleton<IModMenu, ModMenu>();
serviceCollection.AddSingleton<IMediator, Mediator>();
serviceCollection.AddSingleton<IModServices, ModServices>();

serviceCollection.AddScoped<IModContext, ModContext>();
serviceCollection.AddScoped<IPatchService, PatchService>();
serviceCollection.AddScoped<IModMenuService, ModMenuService>();
serviceCollection.AddScoped<IMediationService, MediationService>();
serviceCollection.AddScoped<IModServiceRegistry, ModServiceRegistry>();

serviceCollection.AddSingleton<ICoreService, CoreService>();

Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ StarMap is an actively developed prototype. Check that a mod targets a compatibl
- [Create your first mod](modding/first-mod.md)
- [Browse the modding overview and API](modding/index.md)

The version selector in the header lets you view documentation for a particular major/minor release. `latest` points to the most recently published documentation version.
This site documents the current StarMap release or release-candidate build. Older release snapshots are not currently published.
21 changes: 11 additions & 10 deletions docs/maintainers.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,32 @@ Install the Python documentation dependencies and serve the site:

```powershell
python -m pip install --requirement requirements-docs.txt
python -m mkdocs serve
zensical serve
```

Before opening a pull request, run the same strict build used by CI:

```powershell
python -m mkdocs build --strict
zensical build --clean --strict
```

Keep documentation changes in the same pull request as the feature or behavior change they describe. The PR workflow builds the documentation to catch configuration errors and warnings.

## Versioned publishing
Zensical reads the existing MkDocs-compatible `mkdocs.yml`, so the project does not need a second configuration file. Do not add MkDocs-only plugins such as Mike: Zensical currently has no native version selector, so older release snapshots are not maintained.

The release workflow runs from semantic-version tags in `X.Y.Z` format. It publishes a documentation snapshot when the major/minor portion changes from the previous reachable release tag. Mike names the snapshot `X.Y`, preserves older snapshots, updates the `latest` alias, and makes that alias the site's default. Patch releases do not create additional snapshots. If the `gh-pages` branch does not exist yet, the first tagged release bootstraps the documentation site.
## Publishing

The workflow publishes documentation after the release ZIP has been created. It pushes generated site files to the `gh-pages` branch; the source Markdown remains in `docs/`.
The Pages artifact contains the latest tagged release at the site root and documentation from `main` under `/rc/`. Release tags update the stable site; pushes to `main` update the RC site. Both trees are rebuilt for each deployment because GitHub Pages deploys the complete artifact rather than merging individual paths.

After the first deployment, configure GitHub Pages once in the repository settings:
The release workflow publishes after the release ZIP has been created, and the RC workflow publishes after a push to `main`. Both use GitHub Pages artifacts; the source Markdown remains in `docs/`.

Configure GitHub Pages once in the repository settings:

1. Open **Settings → Pages**.
2. Under **Build and deployment**, select **Deploy from a branch**.
3. Select the `gh-pages` branch and the `/(root)` folder, then save.
2. Under **Build and deployment**, select **GitHub Actions** as the source.

The site URL configured in `mkdocs.yml` is `https://starmaploader.github.io/StarMap/`.

## Theme maintenance
## Zensical configuration

The current Material for MkDocs release announces end-of-life on May 5, 2027. Mike provides the versioning workflow separately from the theme, so review theme support before that date. The [Zensical MkDocs compatibility guide](https://zensical.org/docs/compatibility/mkdocs/migration/) describes the successor project's migration path.
The [Zensical MkDocs compatibility guide](https://zensical.org/docs/compatibility/mkdocs/migration/) explains how Zensical reads `mkdocs.yml`. Zensical silently ignores unsupported MkDocs plugins, so verify plugin support before adding one.
Loading
Loading