Deploy SDK #45
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Deploy SDK | |
| on: | |
| push: | |
| tags: | |
| - 'v*' | |
| - 'release/*' | |
| - 'release-*' | |
| branches: | |
| - main | |
| - opengl | |
| - develop | |
| workflow_dispatch: | |
| inputs: | |
| tag_name: | |
| description: "Release tag / version name (e.g. release/1.0.0 or v1.0.0)" | |
| required: false | |
| default: "" | |
| type: string | |
| create_release: | |
| description: "Publish as official GitHub Release?" | |
| required: true | |
| default: true | |
| type: boolean | |
| is_draft: | |
| description: "Publish as Draft Release?" | |
| required: false | |
| default: false | |
| type: boolean | |
| is_prerelease: | |
| description: "Mark as Pre-release?" | |
| required: false | |
| default: false | |
| type: boolean | |
| concurrency: | |
| group: deploy-sdk-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| CI: true | |
| DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true | |
| DOTNET_CLI_TELEMETRY_OPTOUT: true | |
| DOTNET_NOLOGO: true | |
| NODE_OPTIONS: "--no-deprecation" | |
| jobs: | |
| build: | |
| name: Build SDK (${{ matrix.os }}) | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| platform: linux | |
| preset: linux-clang | |
| config: Release | |
| - os: windows-latest | |
| platform: windows | |
| preset: windows-clang | |
| config: Release | |
| defaults: | |
| run: | |
| shell: bash | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: false | |
| fetch-depth: 1 | |
| - name: Write submodule SHA manifest | |
| run: git submodule status | tee submodule-shas.txt | |
| - name: Cache submodules | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| .git/modules | |
| thirdparty | |
| key: submodules-${{ runner.os }}-${{ hashFiles('submodule-shas.txt') }} | |
| restore-keys: | | |
| submodules-${{ runner.os }}- | |
| - name: Init submodules | |
| run: git submodule update --init --recursive --depth 1 | |
| - name: Setup MSYS2 (Windows) | |
| if: matrix.os == 'windows-latest' | |
| uses: msys2/setup-msys2@v2 | |
| with: | |
| msystem: MINGW64 | |
| update: false | |
| path-type: inherit | |
| install: mingw-w64-x86_64-clang lld mingw-w64-x86_64-lld mingw-w64-x86_64-gcc mingw-w64-x86_64-make | |
| - name: Prefer MSYS2 toolchain over preinstalled MinGW | |
| if: matrix.os == 'windows-latest' | |
| shell: msys2 {0} | |
| run: | | |
| cygpath -w /mingw64/bin >> "$GITHUB_PATH" | |
| - name: Setup Mold Linker (Linux) | |
| if: matrix.os == 'ubuntu-latest' | |
| uses: rui314/setup-mold@v1 | |
| - name: Install Linux Dependencies | |
| if: matrix.os == 'ubuntu-latest' | |
| run: | | |
| set -euo pipefail | |
| sudo apt-get update | |
| sudo apt-get install -y --no-install-recommends \ | |
| build-essential \ | |
| xorg-dev \ | |
| libgl1-mesa-dev libglu1-mesa-dev \ | |
| libasound2-dev \ | |
| pkg-config libgtk-3-dev libdrm-dev libgbm-dev \ | |
| xvfb libxkbcommon-x11-0 libgl1-mesa-dri mesa-utils \ | |
| clang-18 clang++-18 libc++-18-dev libc++abi-18-dev lld-18 llvm-18 | |
| echo "CC=clang-18" >> $GITHUB_ENV | |
| echo "CXX=clang++-18" >> $GITHUB_ENV | |
| echo "LDFLAGS=-fuse-ld=mold" >> $GITHUB_ENV | |
| - name: Get CMake and Ninja | |
| uses: lukka/get-cmake@latest | |
| - name: Setup Compiler Cache | |
| uses: hendrikmuhs/ccache-action@v1.2 | |
| with: | |
| variant: ${{ matrix.os == 'windows-latest' && 'sccache' || 'ccache' }} | |
| key: SDK-${{ matrix.preset }}-${{ matrix.config }} | |
| max-size: 5G | |
| - name: Setup Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| cache: 'pip' | |
| - name: Install Python dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| python -m pip install jinja2 | |
| - name: Setup .NET SDK | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: | | |
| 9.0.x | |
| 10.0.x | |
| - name: Get current version/date | |
| id: info | |
| shell: bash | |
| run: | | |
| echo "date=$(date +'%d.%m.%y')" >> $GITHUB_OUTPUT | |
| if [[ -n "${{ inputs.tag_name }}" ]]; then | |
| RAW_TAG="${{ inputs.tag_name }}" | |
| TAG_VERSION="${RAW_TAG#release/}" | |
| TAG_VERSION="${TAG_VERSION#release-}" | |
| TAG_VERSION="${TAG_VERSION#v}" | |
| elif [[ "${GITHUB_REF}" == refs/tags/* ]]; then | |
| RAW_TAG="${GITHUB_REF#refs/tags/}" | |
| TAG_VERSION="${RAW_TAG#release/}" | |
| TAG_VERSION="${TAG_VERSION#release-}" | |
| TAG_VERSION="${TAG_VERSION#v}" | |
| else | |
| BRANCH_NAME="${GITHUB_REF#refs/heads/}" | |
| CLEAN_BRANCH="${BRANCH_NAME//\//-}" | |
| SHORT_SHA="${GITHUB_SHA:0:7}" | |
| TAG_VERSION="${CLEAN_BRANCH}-${SHORT_SHA}" | |
| RAW_TAG="v${TAG_VERSION}" | |
| fi | |
| echo "raw_tag=${RAW_TAG}" >> $GITHUB_OUTPUT | |
| echo "version=${TAG_VERSION}" >> $GITHUB_OUTPUT | |
| - name: Configure CMake | |
| run: | | |
| set -euo pipefail | |
| PYTHON_EXE=$(python3 -c "import sys; print(sys.executable)" 2>/dev/null || python -c "import sys; print(sys.executable)") | |
| launcher="sccache" | |
| if [[ "${{ matrix.os }}" == "ubuntu-latest" ]]; then | |
| launcher="ccache" | |
| fi | |
| mkdir -p "build/${{ matrix.preset }}" | |
| cmake --preset ${{ matrix.preset }} \ | |
| -DCMAKE_C_COMPILER_LAUNCHER=$launcher \ | |
| -DCMAKE_CXX_COMPILER_LAUNCHER=$launcher \ | |
| -DPython_EXECUTABLE="${PYTHON_EXE}" \ | |
| -DPython3_EXECUTABLE="${PYTHON_EXE}" \ | |
| -DCH_CI=ON \ | |
| -DENABLE_UNITY_BUILD=OFF \ | |
| -DENABLE_PCH=OFF \ | |
| -DBUILD_TESTS=OFF \ | |
| 2>&1 | tee "build/${{ matrix.preset }}/configure.log" | |
| - name: Build binaries | |
| run: | | |
| set -euo pipefail | |
| cmake --build "build/${{ matrix.preset }}" --config ${{ matrix.config }} --parallel \ | |
| 2>&1 | tee "build/${{ matrix.preset }}/build_${{ matrix.config }}.log" | |
| - name: Sync resources | |
| run: | | |
| set -euo pipefail | |
| python tools/sync_resources.py --root . --bin "build/${{ matrix.preset }}/bin" --config ${{ matrix.config }} | |
| - name: Pack game resources into .pack | |
| run: | | |
| set -euo pipefail | |
| WS="${{ github.workspace }}" | |
| BIN_DIR="$WS/build/${{ matrix.preset }}/bin/${{ matrix.config }}" | |
| if [[ ! -d "$BIN_DIR" ]]; then | |
| BIN_DIR="$WS/build/${{ matrix.preset }}/bin" | |
| fi | |
| # Find packer executable (multi-config or single-config) | |
| PACKER="$BIN_DIR/packer" | |
| [[ -f "$PACKER.exe" ]] && PACKER="$PACKER.exe" | |
| if [[ ! -f "$PACKER" ]]; then | |
| # Try searching the whole build tree | |
| PACKER=$(find "$WS/build/${{ matrix.preset }}" -name "packer" -o -name "packer.exe" 2>/dev/null | head -1) | |
| fi | |
| if [[ ! -f "$PACKER" ]]; then | |
| echo "ERROR: packer executable not found" >&2 | |
| exit 1 | |
| fi | |
| echo "Using packer: $PACKER" | |
| # Find .chproject file for the active game | |
| CHPROJECT="" | |
| for f in "$WS/game/chaineddecos"/*.chproject; do | |
| [[ -f "$f" ]] && CHPROJECT="$f" && break | |
| done | |
| # Create resources.pack from assets/ + resources/ | |
| python "$WS/tools/create_pack.py" \ | |
| --packer "$PACKER" \ | |
| --output "$BIN_DIR" \ | |
| ${CHPROJECT:+--chproject "$CHPROJECT"} \ | |
| --assets "$WS/game/chaineddecos/assets" \ | |
| --resources "$WS/resources" \ | |
| --split-mb 0 \ | |
| --zip-threshold 5 \ | |
| --data-version 0 | |
| - name: Package Game and Editor bundles | |
| id: package | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| VERSION="${{ steps.info.outputs.version }}" | |
| PLATFORM="${{ matrix.platform }}" | |
| IS_WINDOWS="${{ matrix.os == 'windows-latest' }}" | |
| WS="${{ github.workspace }}" | |
| # Locate the build output directory (multi-config vs single-config generators) | |
| BIN_DIR="$WS/build/${{ matrix.preset }}/bin/${{ matrix.config }}" | |
| if [[ ! -d "$BIN_DIR" ]]; then | |
| BIN_DIR="$WS/build/${{ matrix.preset }}/bin" | |
| fi | |
| # Collect runtime DLLs / shared libs needed by both executables | |
| RUNTIME_FILES=() | |
| for f in "$BIN_DIR"/*.dll "$BIN_DIR"/*.so "$BIN_DIR"/*.so.* "$BIN_DIR"/*.dylib; do | |
| [[ -f "$f" ]] && RUNTIME_FILES+=("$f") | |
| done | |
| # Helper: assemble a bundle directory and compress it | |
| make_bundle() { | |
| local BUNDLE_NAME="$1" # e.g. ChainedDecos-Game | |
| local BUNDLE_DIR="$WS/pkg-$BUNDLE_NAME" | |
| shift | |
| mkdir -p "$BUNDLE_DIR" | |
| # Copy files/dirs passed as remaining arguments | |
| while (( "$#" )); do | |
| local SRC="$1"; shift | |
| if [[ -e "$SRC" ]]; then | |
| cp -r "$SRC" "$BUNDLE_DIR/" | |
| fi | |
| done | |
| # Copy runtime DLLs / shared libs | |
| for f in "${RUNTIME_FILES[@]:-}"; do | |
| [[ -f "$f" ]] && cp "$f" "$BUNDLE_DIR/" | |
| done | |
| # Copy .NET managed assemblies (Coral + Chained scripting) | |
| for f in "$BIN_DIR"/*.dll "$BIN_DIR"/*.runtimeconfig.json "$BIN_DIR"/*.deps.json; do | |
| [[ -f "$f" ]] && cp "$f" "$BUNDLE_DIR/" 2>/dev/null || true | |
| done | |
| local ARCHIVE_PATH="$WS/${BUNDLE_NAME}-${VERSION}-${PLATFORM}" | |
| if [[ "$IS_WINDOWS" == "true" ]]; then | |
| pushd "$BUNDLE_DIR" > /dev/null | |
| powershell -Command "Compress-Archive -Path * -DestinationPath '${ARCHIVE_PATH}.zip' -Force" | |
| popd > /dev/null | |
| echo "${BUNDLE_NAME}-${VERSION}-${PLATFORM}.zip" | |
| else | |
| pushd "$BUNDLE_DIR" > /dev/null | |
| tar -czf "${ARCHIVE_PATH}.tar.gz" . | |
| popd > /dev/null | |
| echo "${BUNDLE_NAME}-${VERSION}-${PLATFORM}.tar.gz" | |
| fi | |
| } | |
| # ── Game bundle: executable + .pack + scripts/ ───────────────────── | |
| GAME_EXE="" | |
| [[ -f "$BIN_DIR/ChainedDecos.exe" ]] && GAME_EXE="$BIN_DIR/ChainedDecos.exe" | |
| [[ -f "$BIN_DIR/ChainedDecos" ]] && GAME_EXE="$BIN_DIR/ChainedDecos" | |
| # Collect .pack files created by create_pack.py | |
| GAME_PACKS=() | |
| for f in "$BIN_DIR"/resources*.pack; do | |
| [[ -f "$f" ]] && GAME_PACKS+=("$f") | |
| done | |
| # Locate .chproject for the game | |
| CHPROJECT="" | |
| for f in "$WS/game/chaineddecos"/*.chproject; do | |
| [[ -f "$f" ]] && CHPROJECT="$f" && break | |
| done | |
| GAME_ARCHIVE=$(make_bundle "ChainedDecos-Game" \ | |
| "$GAME_EXE" \ | |
| "${GAME_PACKS[@]}" \ | |
| ${CHPROJECT:+"$CHPROJECT"} \ | |
| "$BIN_DIR/scripts" \ | |
| "$BIN_DIR/nethost") | |
| echo "game_archive=$GAME_ARCHIVE" >> $GITHUB_OUTPUT | |
| # ── Editor bundle: executable + engine resources only (no game assets) ── | |
| EDITOR_EXE="" | |
| [[ -f "$BIN_DIR/ChainedEditor.exe" ]] && EDITOR_EXE="$BIN_DIR/ChainedEditor.exe" | |
| [[ -f "$BIN_DIR/ChainedEditor" ]] && EDITOR_EXE="$BIN_DIR/ChainedEditor" | |
| EDITOR_ARCHIVE=$(make_bundle "ChainedEditor" \ | |
| "$EDITOR_EXE" \ | |
| "$BIN_DIR/resources" \ | |
| "$BIN_DIR/editor_settings.yaml" \ | |
| "$BIN_DIR/nethost") | |
| echo "editor_archive=$EDITOR_ARCHIVE" >> $GITHUB_OUTPUT | |
| - name: Upload Game artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: Game-${{ matrix.platform }} | |
| path: ${{ github.workspace }}/${{ steps.package.outputs.game_archive }} | |
| retention-days: 30 | |
| - name: Upload Editor artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: Editor-${{ matrix.platform }} | |
| path: ${{ github.workspace }}/${{ steps.package.outputs.editor_archive }} | |
| retention-days: 30 | |
| release: | |
| name: Create GitHub Release | |
| runs-on: ubuntu-latest | |
| needs: build | |
| if: >- | |
| (github.event_name == 'workflow_dispatch' && (inputs.create_release == true || inputs.create_release == 'true')) || | |
| (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')) | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Get release info | |
| id: info | |
| run: | | |
| echo "date=$(date +'%d.%m.%y')" >> $GITHUB_OUTPUT | |
| if [[ -n "${{ inputs.tag_name }}" ]]; then | |
| RAW_TAG="${{ inputs.tag_name }}" | |
| TAG_VERSION="${RAW_TAG#release/}" | |
| TAG_VERSION="${TAG_VERSION#release-}" | |
| TAG_VERSION="${TAG_VERSION#v}" | |
| elif [[ "${GITHUB_REF}" == refs/tags/* ]]; then | |
| RAW_TAG="${GITHUB_REF#refs/tags/}" | |
| TAG_VERSION="${RAW_TAG#release/}" | |
| TAG_VERSION="${TAG_VERSION#release-}" | |
| TAG_VERSION="${TAG_VERSION#v}" | |
| else | |
| BRANCH_NAME="${GITHUB_REF#refs/heads/}" | |
| CLEAN_BRANCH="${BRANCH_NAME//\//-}" | |
| SHORT_SHA="${GITHUB_SHA:0:7}" | |
| TAG_VERSION="${CLEAN_BRANCH}-${SHORT_SHA}" | |
| RAW_TAG="v${TAG_VERSION}" | |
| fi | |
| echo "raw_tag=${RAW_TAG}" >> $GITHUB_OUTPUT | |
| echo "version=${TAG_VERSION}" >> $GITHUB_OUTPUT | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: release-assets | |
| pattern: "{Game,Editor}-*" | |
| merge-multiple: true | |
| - name: Create Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| name: "Chained Engine v${{ steps.info.outputs.version }}" | |
| tag_name: "${{ steps.info.outputs.raw_tag }}" | |
| files: release-assets/* | |
| body: | | |
| ### Chained Engine v${{ steps.info.outputs.version }} | |
| - **Build Date**: ${{ steps.info.outputs.date }} | |
| - **Platforms**: Linux (clang), Windows (clang) | |
| #### Downloads | |
| | File | Platform | Contents | | |
| |------|----------|----------| | |
| | `ChainedDecos-Game-*-windows.zip` | Windows | Game executable + .chproject + resources.pack + scripts + nethost/ + DLLs | | |
| | `ChainedDecos-Game-*-linux.tar.gz` | Linux | Game executable + .chproject + resources.pack + scripts + nethost/ | | |
| | `ChainedEditor-*-windows.zip` | Windows | Editor executable + engine resources + nethost/ + DLLs | | |
| | `ChainedEditor-*-linux.tar.gz` | Linux | Editor executable + engine resources + nethost/ | | |
| > **Game** — packaged with `resources.pack` (assets + engine resources compressed), `.chproject` project config, `scripts/` (C# assemblies), and `nethost/` (.NET hosting). On Linux, requires .NET 9+ runtime. | |
| > **Editor** — includes the editor binary, engine resources (shaders, fonts, icons), and `nethost/` (.NET hosting). On Linux, requires .NET 9+ runtime. | |
| #### Requirements | |
| - Windows: no extra dependencies (all DLLs included) | |
| - Linux: requires .NET 9+ runtime for C# scripting | |
| draft: ${{ inputs.is_draft || false }} | |
| prerelease: ${{ inputs.is_prerelease || false }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |