A universal workflow to build SKSE mods that are based on powerof3/CommonLibSSE and/or alandtse/CommonLibVR.
- ✅ Effortless - doesn't require specific GitHub Actions knowledge, easy to setup.
- ✅ Builds all supported variants: SE, AE, AE (1.6.353), VR.
- ✅ Include PDB files along with DLLs for easier debugging.
- ✅ Generates FOMOD.
- ✅ Publishes FOMOD installer to GitHub Releases.
- ⬛ Publishes FOMOD installer directly to Nexus mod page (Coming soon-ish).
- Setting up the workflow
- Examples
- SSH
- CMake Configuration
- Building AE 1.6.353 (pre 1.6.629 variant)
- FOMOD Customizations
- Publishing
- Extending The Workflow
The workflow tries to be as unintrusive to the actual build process as possible and has only few requirements:
- Add powerof3/CommonLibSSE and/or alandtse/CommonLibVR as git submodules:
git submodule add extern/CommonLibSSE https://github.com/powerof3/CommonLibSSE.git
git submodule add extern/CommonLibVR https://github.com/alandtse/CommonLibVR.git
Note: Make sure to point CMake to submodules paths in
CMakeLists.txt
.
- Set
NAME
andVERSION
variables as cache variables inCMakeLists.txt
. (See Binary Name and Version for other options):
set(NAME "my_skse_mod" CACHE STRING "")
set(VERSION 1.0.0 CACHE STRING "")
- Add a workflow file at the repository's root
.github/workflows/main.yml
:
name: Main
on:
push:
branches: '**'
tags: '*'
concurrency:
group: ${{ github.ref }}
cancel-in-progress: true
jobs:
run:
uses: adya/pack-skse-mod/.github/workflows/pack.yml@main
with:
FOMOD_MOD_NAME: "My SKSE Mod"
FOMOD_MOD_AUTHOR: "The Author"
Note: Default values for configuration of
pack.yml
workflow is based on powerof3 projects. This includes preset names, build directories, variable names, etc. If your project uses different naming you need to also specify custom values as described below.
Here are some examples of configured workflows:
- AnimObjectSwapper: Basic configuration, builds SE, AE, VR, packs installer into 7z with PDB files.
- EnhancedReanimation: Does everything that AnimObjectSwapper + supports AE 1.6.353 as a separate variant.
- Spell Perk Item Distributor: Showcases custom project path, configuration of the FOMOD, provides image and links to changelog and description files.
If your repository or one of it's submodules uses an SSH, you may specify an SSH key that workflow will use to checkout such repositories.
Note: You should provide your SSH key as a Secret.
jobs:
run:
uses: adya/pack-skse-mod/.github/workflows/pack.yml@main
secrets:
GIT_SSH_KEY: ${{ secrets.MY_SSH_KEY }}
The workflow supports both Configuration Presets and Build Presets.
By default it is configured as the following:
jobs:
run:
uses: adya/pack-skse-mod/.github/workflows/pack.yml@main
with:
CMAKE_SE_CONFIG_PRESET: 'vs2022-windows-vcpkg-se'
CMAKE_SE_BUILD_PRESET: ''
CMAKE_AE_CONFIG_PRESET: 'vs2022-windows-vcpkg-ae'
CMAKE_AE_BUILD_PRESET: ''
CMAKE_VR_CONFIG_PRESET: 'vs2022-windows-vcpkg-vr'
CMAKE_VR_BUILD_PRESET: ''
Note: Build Presets are an opt-in feature, if these presets are not specified then the workflow will perform
cmake --build $BINARY_DIR --config Release
. If yourCMakePresets.json
provide build presets you can set them via these parameters and the workflow will docmake --build --preset $BUILD_PRESET
instead.
The workflow checks whether specified Configuration Presets are present in
CMakePresets.json
and builds only variants that have existing presets. You might explicitly disable building of any variant by passing blank string to corresponding Configuration Preset.
By default workflow assumes that Release
build configuration will be used to build the project. This configuration is also used to determine path to the build artifacts that will be packed into FOMOD.
When you want to use build configurations other than Release
you can specify it with a CMAKE_BUILD_CONFIGURATION
variable.
For example, if you want to create both Release
and Debug
variants of the mod you can do this in your main.yaml
:
jobs:
release:
uses: adya/pack-skse-mod/.github/workflows/pack.yml@main
with:
CMAKE_BUILD_CONFIGURATION: 'Release'
FOMOD_MOD_NAME: "My SKSE Mod"
FOMOD_MOD_AUTHOR: "The Author"
debug:
uses: adya/pack-skse-mod/.github/workflows/pack.yml@main
with:
CMAKE_BUILD_CONFIGURATION: 'Debug'
FOMOD_MOD_NAME: "My SKSE Mod DEBUG"
FOMOD_MOD_AUTHOR: "The Author"
This will produce two FOMOD installers with corresponding artifacts.
A directory where CMake outputs build files. By default the workfow sets binary directories for variants as follows:
jobs:
run:
uses: adya/pack-skse-mod/.github/workflows/pack.yml@main
with:
CMAKE_SE_BINARY_DIR: 'build'
CMAKE_AE_BINARY_DIR: 'buildae'
CMAKE_VR_BINARY_DIR: 'buildvr'
If you have different binary directories you should provide them via these parameters.
When plugin's project files do not reside directly in the repository's root you should specify a path to it using CMAKE_PROJECT_DIR
.
This will point vcpkg
to %repository_root%/CMAKE_PROJECT_DIR/vcpkg.json and append CMAKE_PROJECT_DIR as a suffix to CMAKE_XX_BINARY_DIR
variables, so that build products will be searched for in %repository_root%/CMAKE_XX_BINARY_DIR/CMAKE_PROJECT_DIR/Release/:
jobs:
run:
uses: adya/pack-skse-mod/.github/workflows/pack.yml@main
with:
CMAKE_PROJECT_DIR: ""
For example, SPID plugin's project files are located at
./SPID
, so it usesCMAKE_PROJECT_DIR: "SPID"
The workflow retrives information such as built product's name and version using CMake's cache. It is used to later generate FOMOD and publish the mod.
By default it looks for common variables NAME
and VERSION
.
jobs:
run:
uses: adya/pack-skse-mod/.github/workflows/pack.yml@main
with:
CMAKE_BINARY_NAME_VAR: "NAME"
CMAKE_BINARY_VERSION_VAR: "VERSION"
You may provide any other cached variable to be used as NAME or VERSION respectfully.
Note: It is important that these variables are defined as
CACHE
variables, otherwise workflow will fail to read them.
You might also explicilty specify values for NAME
and VERSION
if you don't want to make CMake variables cached.
Though, you'll need to update these values (at least VERSION
) in your workflow every time you change the version.
jobs:
run:
uses: adya/pack-skse-mod/.github/workflows/pack.yml@main
with:
CMAKE_BINARY_NAME: "my_skse_mod"
CMAKE_BINARY_VERSION: "1.0.0"
This is another opt-in feature. It is disabled by default.
Support for AE 1.6.353 is implemented through checkouting a dedicated branch that is built against powerof3/CommonLibSSE's dev-1.6.353 branch.
You can opt-in by providing such branch like this:
jobs:
run:
uses: adya/pack-skse-mod/.github/workflows/pack.yml@main
with:
AE_353_BRANCH: "ae_353_branch" # name of the branch that can be built with dev-1.6.353
If you don't have a separate branch for 1.6.353 and your main branch supports all AE versions, then you can specify workflow's current branch to enable this feature:
jobs:
run:
uses: adya/pack-skse-mod/.github/workflows/pack.yml@main
with:
AE_353_BRANCH: "${{ github.ref }}"
Note: You can also change CommonLibSSE's branch to be used for 1.6.353 build by specifying it with
AE_353_COMMON_LIB_BRANCH
parameter.
Note: This variant uses AE-related build parameters of the workflow.
The ultimate artifact produced by this workflow is a FOMOD installer containing all built variants that is ready to be published to Nexus or any other platform.
The FOMOD has built-in automated detection of appropriate game version and pre-selects correct option for user.
The metadata that is provided in info.xml
:
jobs:
run:
uses: adya/pack-skse-mod/.github/workflows/pack.yml@main
with:
FOMOD_MOD_NAME: "My SKSE Mod"
FOMOD_MOD_AUTHOR: "The Author"
Note: Version is picked up from binary's version, so there is no dedicated
FOMOD_MOD_VERSION
.
Optionally you can specify id of your mod on Nexus page. It will be used to provide a URL to your mod in FOMOD.
jobs:
run:
uses: adya/pack-skse-mod/.github/workflows/pack.yml@main
with:
FOMOD_MOD_NEXUS_ID: "1111"
Source paths are paths that specify where installable files are located within FOMOD. These paths are relative to the installer's root.
By default, workflow is preconfigured for a common installer template with the following structure:
installer
├───Required*
├───SE
│ └───SKSE
│ └───Plugins
├───AE353
│ └───SKSE
│ └───Plugins
├───AE
│ └───SKSE
│ └───Plugins
└───VR
└───SKSE
└───Plugins
This corresponds to the following configuration, that you may choose to customize:
jobs:
run:
uses: adya/pack-skse-mod/.github/workflows/pack.yml@main
with:
FOMOD_VR_PATH: "VR/SKSE/Plugins"
FOMOD_SE_PATH: "SE/SKSE/Plugins"
FOMOD_AE_PATH: "AE/SKSE/Plugins"
FOMOD_AE353_PATH: "AE353/SKSE/Plugins"
Note: * 'Required' is an optional path and configured with
FOMOD_REQUIRED_SRC_PATH
. (See Additional required installation
Destinations paths are paths that specify where files should be installed to. ****.
Note: These paths are relative to game's
Data/
folder, so you should not start destination paths with "Data/" as this might cause issues in some mod managers. For instance, Vortex handles such paths correctly, but MO2 does not.
Installation path for all options is configured with FOMOD_DST_PATH
and by default is set to SKSE/Plugins
.
You also may have additional files you'd want to include along with plugin variants.
For this case you can specify a directory containing files that you want to be always installed using FOMOD_REQUIRED_INSTALLATION_DIR
parameter. This path is relative to your repository's root where you place the required files. The files in specified directory will be copied into FOMOD's installer to directory specified with in FOMOD_REQUIRED_SRC_PATH
.
By default,
FOMOD_REQUIRED_INSTALLATION_DIR
is blank, so that required installation is skipped.
Aditionally, you may customize where required files will be placed inside FOMOD and the default installation path within Game's folder.
Here is an example of all relevant inputs:
jobs:
run:
uses: adya/pack-skse-mod/.github/workflows/pack.yml@main
with:
FOMOD_REQUIRED_INSTALLATION_DIR: "FOMOD/Required Files"
FOMOD_REQUIRED_SRC_PATH: "Required"
Note that contents of
FOMOD_REQUIRED_INSTALLATION_DIR
will be copied to game's Data folder (this is the defaultFOMOD_REQUIRED_DST_PATH
).
If you'd like to distribute your mod with PDB files, you may specify FOMOD_INCLUDE_PDB
option. This will include PDB files into FOMOD and install them along with DLLs.
jobs:
run:
uses: adya/pack-skse-mod/.github/workflows/pack.yml@main
with:
FOMOD_INCLUDE_PDB: true
Things like installer's title, option names, descriptions, minimum required game versions are all configurable using the following parameters:
jobs:
run:
uses: adya/pack-skse-mod/.github/workflows/pack.yml@main
with:
FOMOD_TITLE: 'Main'
FOMOD_GROUP_NAME: 'DLL'
FOMOD_SE_NAME: 'SSE v1.5.97 ("Special Edition")'
FOMOD_SE_DESCR: 'Select this if you are using Skyrim Special Edition v1.5.97.'
FOMOD_SE_MIN_GAME_VERSION: '1.5.97.0'
FOMOD_AE353_NAME: 'SSE v1.6.353 ("Anniversary Edition")'
FOMOD_AE353_DESCR: 'Select this if you are using Skyrim Anniversary Edition v1.6.353 or lower.'
FOMOD_AE353_MIN_GAME_VERSION: '1.6.317.0'
FOMOD_AE_NAME: 'SSE v1.6.629+ ("Anniversary Edition")'
FOMOD_AE_DESCR: 'Select this if you are using Skyrim Anniversary Edition v1.6.629 or higher (latest update).'
FOMOD_AE_MIN_GAME_VERSION: '1.6.629.0'
FOMOD_VR_NAME: 'VR v1.4.15 ("Skyrim VR")'
FOMOD_VR_DESCR: 'Select this if you are using Skyrim VR v1.4.15.'
FOMOD_VR_MIN_GAME_VERSION: '1.4.15.0'
In addition to textual content of the installer you might as well provide cover images for each installation option. Put them inside your repository and specify paths to these images. Path is relative to repository's root.
jobs:
run:
uses: adya/pack-skse-mod/.github/workflows/pack.yml@main
with:
FOMOD_SE_IMAGE: 'FOMOD/images/cover_se.png'
FOMOD_AE353_IMAGE: 'FOMOD/images/cover_ae353.png'
FOMOD_AE_IMAGE: 'FOMOD/images/cover_ae.png'
FOMOD_VR_IMAGE: 'FOMOD/images/cover_vr.png'
Note that images are copied to dedicated FOMOD's folder (fomod/images), so make sure your images have distrinct names, otherwise they'll be overwritten by one another.
If you don't have separate images for each variant (or only for few of them) you might instead provide FOMOD_DEFAULT_IMAGE
which will be used when variant's image is not provided.
For example here all variants except VR will use default 'cover.png', but VR will have it's own image:
jobs:
run:
uses: adya/pack-skse-mod/.github/workflows/pack.yml@main
with:
FOMOD_DEFAULT_IMAGE: 'FOMOD/images/cover.png'
FOMOD_VR_IMAGE: 'FOMOD/images/cover_vr.png'
When you push a new tag
to GitHub, the workflow will perform an additional publishing step.
Even though the workflow doesn't use tag's name as a version, you should still follow Semantic Versioning for the sake of clarity :)
When publishing FOMOD installer you may specify what archiver to use. Currently supported are zip
and 7z
:
jobs:
run:
uses: adya/pack-skse-mod/.github/workflows/pack.yml@main
with:
PUBLISH_ARCHIVE_TYPE: '7z'
Note: By deafult workflow will use
PUBLISH_ARCHIVE_TYPE: 'zip'
.
Note: This option is only relevant for publishing, since Artifacts produced by separate jobs are packed into zip automatically by GitHub Actions.
One of the destinations where package can be published is GitHub's Releases. Once workflow publishes there you'll be able to download the installer's archive from Releases page of your repository.
You may also specify changelog and description of the mod file. They'll be added to that Release:
jobs:
run:
uses: adya/pack-skse-mod/.github/workflows/pack.yml@main
with:
PUBLISH_MOD_DESCRIPTION_FILE: 'FOMOD/description.txt'
PUBLISH_MOD_CHANGELOG_FILE: 'FOMOD/changelog.txt'
This one is work in progress and is not included in @main. It's also highliy experimental :) so if you feel adventurous you might try it on @nexus-upload
DISCLAIMER: As of now there is no official NexusMods API to upload files, this step uses known, yet still unofficial tool to perform upload.
This will publish produced installer's package directly to your mod's Nexus page, replacing the Main file that had the latest version. So if your mod had several published Main files the first one with largest version will be update.
VERSION
variable from youCMakeLists.txt
will be used as a new version of the uploaded file.
- If you don't have Nexus's Personal API Key, then Create one.
- Add this key to your repositor's secrets. Refer to GitHub Secrets docs. 1.1. or if you plan on using it in multiple repositories, consider creating your own organization and moving all repos there, so they'll share the same Organization's secrets.
- Get Cookies from your active Nexus session. This can be done in various ways, for example, using *Get cookies.txt for Google Chrom (or Microsoft Edge)
- Once set you're ready to enable publishing on Nexus step:
jobs:
run:
uses: adya/pack-skse-mod/.github/workflows/pack.yml@main
with:
FOMOD_MOD_NEXUS_ID: '11111'
PUBLISH_GAME_NAME: 'skyrimspecialedition'
PUBLISH_MOD_DESCRIPTION_FILE: 'FOMOD/description.txt'
PUBLISH_MOD_CHANGELOG_FILE: 'FOMOD/changelog.txt'
secrets:
PUBLISH_NEXUS_API_KEY: ${{ secrets.YOUR_NEXUS_API_KEY_SECRET }}
PUBLISH_NEXUS_COOKIE: ${{ secrets.YOUR_NEXUS_COOKIES_SECRET }}
Replace
YOUR_NEXUS_API_KEY_SECRET
andYOUR_NEXUS_COOKIES_SECRET
with corresponding secrets that you've created.
The workflow exposes all useful data as outputs. This allows building your custom workflows on top of pack-skse-mod.
Here are currently available outputs:
PRODUCT_VERSION
FOMOD_INSTALLER
SE_ARTIFACT_NAME
AE_ARTIFACT_NAME
AE353_ARTIFACT_NAME
VR_ARTIFACT_NAME
which can be used in your workflow like this:
name: Main
on:
push:
branches: '**'
tags: '*'
concurrency:
group: ${{ github.ref }}
cancel-in-progress: true
jobs:
run:
uses: adya/pack-skse-mod/.github/workflows/pack.yml@main
with:
FOMOD_MOD_NAME: "My SKSE Mod"
FOMOD_MOD_AUTHOR: "The Author"
post-discord-notification:
runs-on: windows-latest
needs: run
steps:
- name: Print version
run: echo "Built Version ${{needs.run.outputs.PRODUCT_VERSION}}"