Publishing a release

Build and distribute a C++ plugin through GitHub Releases.

A C++ plugin ships as a compiled shared library - a .dll on Windows, a .so on Linux. There's no central index like PyPI, so you distribute the built files and users drop them into their server's plugins/ folder. The usual home for those files is GitHub Releases. There are two ways to get there: let GitHub Actions build and publish a release from your repository, or build and upload from your own machine.

Publish from a GitHub repository

The recommended setup publishes straight from your repo, so every release is built on clean CI runners for both platforms - no "works on my machine" surprises.

The example plugin ships a ready-made release.yml workflow. Triggered from the repo's Actions tab with a version number, it bumps the version in CMakeLists.txt, updates the changelog, tags the release, and creates a GitHub Release - then builds the library on Windows and Linux and attaches each binary to that release.

Two matrix jobs do the building, each checking out the freshly pushed tag, compiling with CMake, and uploading the result:

.github/workflows/release.yml
  build_windows:
    needs: [ release ]
    runs-on: windows-2022
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v6
        with:
          ref: v${{ inputs.version }}
      - uses: ilammy/msvc-dev-cmd@v1
      - uses: lukka/get-cmake@latest
      - run: |
          cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo
          cmake --build build
      - env:
          GH_TOKEN: ${{ github.token }}
        run: gh release upload "v${{ inputs.version }}" build/endstone_*.dll build/endstone_*.pdb

The Linux job is the same shape, building with Clang and uploading build/endstone_*.so. The contents: write permission is what lets the workflow push the tag, open the release, and attach the binaries.

Run the workflow from the Actions tab with the version you want (it also takes a dry run option to preview the version bump and changelog without pushing anything). When it finishes, the release is live at https://github.com/<owner>/<repo>/releases with a .dll, .pdb, and .so attached.

Publish from your local machine

If you'd rather cut a release by hand, build the library yourself and upload it.

Build

Build your project for each platform you want to support (see Project setup for the toolchain):

cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo
cmake --build build

The result is a shared library such as endstone_my_plugin.dll (or .so) in build/.

The RelWithDebInfo build type is optimised like a release but keeps debug symbols, so when your plugin crashes on someone's server they can send you the crash dump and the symbols let you trace it back to the exact function and line. On Windows the symbols live in the separate .pdb file (which is why the CI job attaches it alongside the .dll); on Linux they're embedded in the .so. Keep a copy of the symbols for every version you ship - a dump is only as useful as the symbols that match it.

On Linux, a .so links against the glibc version of the machine that built it. A plugin built on a newer distro won't load on a server running an older one - the dynamic loader rejects it with a version 'GLIBC_2.xx' not found error. If your build machine is newer than your server, either build on a distro as old as (or older than) the server, or upgrade the server to a newer distro. This is exactly the trap the GitHub CI path avoids, by building on a fixed, older runner (Ubuntu 22.04) so the binary stays compatible with everything newer - one more reason to prefer it.

Distribute

Attach the built libraries to a GitHub Release, or share them through any download page or plugin listing. To install, users copy the file into path/to/bedrock_server/plugins and restart the server.

A library is platform-specific: build a .dll for Windows servers and a .so for Linux servers. Ship the one matching your users' platform, or both.

On this page