Release binaries #32
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: Release binaries | |
| on: | |
| push: | |
| tags: | |
| - "v*" | |
| workflow_dispatch: | |
| inputs: | |
| tag_name: | |
| description: "Release tag name, for example v2.7.2" | |
| required: true | |
| type: string | |
| permissions: | |
| contents: write | |
| defaults: | |
| run: | |
| shell: bash | |
| env: | |
| BUILD_CONFIG: Release | |
| jobs: | |
| build: | |
| name: Build ${{ matrix.platform }} | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 360 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - platform: windows-x64 | |
| os: windows-2022 | |
| archive_ext: zip | |
| - platform: macos | |
| os: macos-latest | |
| archive_ext: tar.gz | |
| - platform: linux | |
| os: ubuntu-22.04 | |
| archive_ext: tar.gz | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Resolve release version | |
| id: version | |
| run: | | |
| if [ -n "${{ github.event.inputs.tag_name }}" ]; then | |
| VERSION="${{ github.event.inputs.tag_name }}" | |
| else | |
| VERSION="${GITHUB_REF_NAME}" | |
| fi | |
| if [ -z "$VERSION" ]; then | |
| VERSION="dev-${GITHUB_SHA::7}" | |
| fi | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| - name: Build on Linux | |
| if: runner.os == 'Linux' | |
| run: | | |
| bash ./install/install_linux.sh "$BUILD_CONFIG" | |
| - name: Build on macOS | |
| if: runner.os == 'macOS' | |
| run: | | |
| bash ./install/install_macos.sh "$BUILD_CONFIG" | |
| - name: Build on Windows | |
| if: runner.os == 'Windows' | |
| shell: cmd | |
| run: | | |
| call install\install_windows.bat %BUILD_CONFIG% | |
| - name: Build GUICONSOLE on Windows | |
| if: runner.os == 'Windows' | |
| shell: cmd | |
| run: | | |
| call install\install_windows.bat %BUILD_CONFIG% "" GUICONSOLE | |
| - name: Verify server binaries | |
| run: | | |
| test -d kbe/bin/server | |
| find kbe/bin/server -maxdepth 1 -type f | sort | |
| - name: Audit Linux runtime dependencies | |
| if: runner.os == 'Linux' | |
| run: | | |
| set -e | |
| for bin in kbe/bin/server/*; do | |
| if [ -f "$bin" ] && file "$bin" | grep -q "ELF"; then | |
| echo "::group::ldd $bin" | |
| ldd "$bin" | |
| echo "::endgroup::" | |
| fi | |
| done | |
| - name: Bundle Linux runtime libraries | |
| if: runner.os == 'Linux' | |
| run: | | |
| set -euo pipefail | |
| sudo apt-get update -y | |
| sudo apt-get install -y patchelf | |
| lib_dir="kbe/bin/server/lib" | |
| mkdir -p "$lib_dir" | |
| : > /tmp/kbe-runtime-libs.txt | |
| for bin in kbe/bin/server/*; do | |
| if [ -f "$bin" ] && file "$bin" | grep -q "ELF"; then | |
| ldd "$bin" | awk ' | |
| /=> \// { print $3 } | |
| /^[[:space:]]*\// { print $1 } | |
| ' >> /tmp/kbe-runtime-libs.txt | |
| fi | |
| done | |
| sort -u /tmp/kbe-runtime-libs.txt | while read -r lib; do | |
| [ -n "$lib" ] || continue | |
| [ -f "$lib" ] || continue | |
| base="$(basename "$lib")" | |
| case "$base" in | |
| ld-linux*.so.*|libc.so.*|libm.so.*|libdl.so.*|libpthread.so.*|librt.so.*|libresolv.so.*|linux-vdso*.so.*) | |
| continue | |
| ;; | |
| esac | |
| cp -L "$lib" "$lib_dir/" | |
| done | |
| for bin in kbe/bin/server/*; do | |
| if [ -f "$bin" ] && file "$bin" | grep -q "ELF"; then | |
| patchelf --set-rpath '$ORIGIN/lib:$ORIGIN' "$bin" | |
| fi | |
| done | |
| echo "::group::bundled Linux runtime libraries" | |
| find "$lib_dir" -maxdepth 1 -type f -printf '%f\n' | sort || true | |
| echo "::endgroup::" | |
| - name: Audit macOS runtime dependencies | |
| if: runner.os == 'macOS' | |
| run: | | |
| set -e | |
| for bin in kbe/bin/server/*; do | |
| if [ -f "$bin" ] && file "$bin" | grep -q "Mach-O"; then | |
| echo "::group::otool -L $bin" | |
| otool -L "$bin" | |
| echo "::endgroup::" | |
| fi | |
| done | |
| - name: Package binaries | |
| id: package | |
| shell: pwsh | |
| run: | | |
| $version = "${{ steps.version.outputs.version }}" | |
| $platform = "${{ matrix.platform }}" | |
| if ($platform -eq "macos" -or $platform -eq "linux") { | |
| $arch = (uname -m).Trim() | |
| $platform = "$platform-$arch" | |
| } | |
| $packageName = "KBEngine-Nex-$version-$platform" | |
| $stageRoot = Join-Path $PWD "dist" | |
| $stageDir = Join-Path $stageRoot $packageName | |
| $kbeDir = Join-Path $stageDir "kbe" | |
| New-Item -ItemType Directory -Force -Path $kbeDir | Out-Null | |
| foreach ($dir in @("docs")) { | |
| if (Test-Path $dir) { | |
| Copy-Item -Path $dir -Destination $stageDir -Recurse -Force | |
| } | |
| } | |
| foreach ($dir in @("bin", "res", "tools")) { | |
| $source = Join-Path "kbe" $dir | |
| if (Test-Path $source) { | |
| Copy-Item -Path $source -Destination $kbeDir -Recurse -Force | |
| } | |
| } | |
| foreach ($file in @("new_assets.bat", "new_assets.sh", "UPDATE.md", "README.md")) { | |
| if (Test-Path $file) { | |
| Copy-Item -Path $file -Destination $stageDir -Force | |
| } | |
| } | |
| $archivePath = Join-Path $stageRoot "$packageName.${{ matrix.archive_ext }}" | |
| if ("${{ matrix.archive_ext }}" -eq "zip") { | |
| Compress-Archive -Path $stageDir -DestinationPath $archivePath -Force | |
| } else { | |
| tar -czf $archivePath -C $stageRoot $packageName | |
| } | |
| "archive=$archivePath" >> $env:GITHUB_OUTPUT | |
| "archive_name=$(Split-Path $archivePath -Leaf)" >> $env:GITHUB_OUTPUT | |
| - name: Upload build artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ steps.package.outputs.archive_name }} | |
| path: ${{ steps.package.outputs.archive }} | |
| if-no-files-found: error | |
| retention-days: 14 | |
| publish: | |
| name: Publish GitHub Release | |
| needs: build | |
| runs-on: ubuntu-latest | |
| if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' | |
| steps: | |
| - name: Resolve release version | |
| id: version | |
| run: | | |
| if [ -n "${{ github.event.inputs.tag_name }}" ]; then | |
| VERSION="${{ github.event.inputs.tag_name }}" | |
| else | |
| VERSION="${GITHUB_REF_NAME}" | |
| fi | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| - name: Download packaged binaries | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: dist | |
| merge-multiple: true | |
| - name: Generate checksums | |
| run: | | |
| cd dist | |
| sha256sum * > SHA256SUMS.txt | |
| - name: Create or update release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| GH_REPO: ${{ github.repository }} | |
| VERSION: ${{ steps.version.outputs.version }} | |
| run: | | |
| if gh release view "$VERSION" >/dev/null 2>&1; then | |
| gh release upload "$VERSION" dist/* --clobber | |
| else | |
| gh release create "$VERSION" dist/* \ | |
| --title "KBEngine-Nex $VERSION" \ | |
| --notes "Release binaries for Windows, macOS, and Linux." | |
| fi | |
| publish-gitcode: | |
| name: Publish GitCode Release | |
| needs: build | |
| runs-on: ubuntu-latest | |
| if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' | |
| env: | |
| GITCODE_OWNER: KBEngineLab | |
| GITCODE_REPO: KBEngine-Nex | |
| GITCODE_TOKEN: ${{ secrets.GITCODE_TOKEN }} | |
| steps: | |
| - name: Check GitCode token | |
| run: | | |
| if [ -z "${GITCODE_TOKEN}" ]; then | |
| echo "GITCODE_TOKEN not configured, skipping GitCode publish" | |
| exit 0 | |
| fi | |
| - name: Resolve release version | |
| id: version | |
| run: | | |
| if [ -n "${{ github.event.inputs.tag_name }}" ]; then | |
| VERSION="${{ github.event.inputs.tag_name }}" | |
| else | |
| VERSION="${GITHUB_REF_NAME}" | |
| fi | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Push tag to GitCode | |
| env: | |
| VERSION: ${{ steps.version.outputs.version }} | |
| run: | | |
| set -e | |
| git remote add gitcode "https://oauth2:${GITCODE_TOKEN}@gitcode.com/${GITCODE_OWNER}/${GITCODE_REPO}.git" | |
| # Create tag locally if needed (workflow_dispatch doesn't have it yet) | |
| if ! git rev-parse "refs/tags/${VERSION}" >/dev/null 2>&1; then | |
| git tag "${VERSION}" -m "Release ${VERSION}" | |
| fi | |
| # Mirror all refs to gitcode | |
| git push gitcode --all --force | |
| git push gitcode --tags --force | |
| - name: Download packaged binaries | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: dist | |
| merge-multiple: true | |
| - name: Generate checksums | |
| run: | | |
| cd dist | |
| sha256sum * > SHA256SUMS.txt | |
| - name: Create release on GitCode | |
| env: | |
| VERSION: ${{ steps.version.outputs.version }} | |
| run: | | |
| set -e | |
| RESP=$(curl -sS -w "\n%{http_code}" -X POST \ | |
| "https://api.gitcode.com/api/v5/repos/${GITCODE_OWNER}/${GITCODE_REPO}/releases?access_token=${GITCODE_TOKEN}" \ | |
| -H "Content-Type: application/json" \ | |
| -d "{\"tag_name\":\"${VERSION}\",\"name\":\"KBEngine-Nex ${VERSION}\",\"body\":\"Release binaries for Windows, macOS, and Linux.\",\"release_status\":\"latest\"}") | |
| HTTP_CODE=$(echo "$RESP" | tail -1) | |
| BODY=$(echo "$RESP" | sed '$d') | |
| echo "Create release: HTTP $HTTP_CODE" | |
| if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "201" ]; then | |
| echo "Release created" | |
| elif [ "$HTTP_CODE" = "409" ]; then | |
| echo "Release already exists, continuing" | |
| else | |
| echo "Failed: $BODY" | |
| exit 1 | |
| fi | |
| - name: Upload assets to GitCode | |
| env: | |
| VERSION: ${{ steps.version.outputs.version }} | |
| run: | | |
| set -e | |
| for f in dist/*; do | |
| fname=$(basename "$f") | |
| echo "::group::Uploading $fname" | |
| UPLOAD_INFO=$(curl -sS \ | |
| "https://api.gitcode.com/api/v5/repos/${GITCODE_OWNER}/${GITCODE_REPO}/releases/${VERSION}/upload_url?access_token=${GITCODE_TOKEN}&file_name=${fname}") | |
| UPLOAD_URL=$(echo "$UPLOAD_INFO" | jq -r '.url') | |
| if [ -z "$UPLOAD_URL" ] || [ "$UPLOAD_URL" = "null" ]; then | |
| echo "Failed to get upload URL: $UPLOAD_INFO" | |
| exit 1 | |
| fi | |
| # Build -H args from returned headers object | |
| HDR_ARGS="" | |
| while IFS= read -r line; do | |
| [ -z "$line" ] && continue | |
| HDR_ARGS="$HDR_ARGS -H \"$line\"" | |
| done < <(echo "$UPLOAD_INFO" | jq -r '.headers | to_entries[] | "\(.key): \(.value)"') | |
| eval "curl -sS -X PUT \"$UPLOAD_URL\" $HDR_ARGS --data-binary @\"$f\" -w '\nHTTP %{http_code}'" | |
| echo | |
| echo "::endgroup::" | |
| done |