diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9a339ee..bdeaa09 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,54 +16,92 @@ jobs: NUGET_PACKAGES: D:\nuget\packages steps: - - name: Checkout - uses: actions/checkout@v6 - with: - submodules: 'recursive' + - name: Checkout + uses: actions/checkout@v6 + with: + submodules: "recursive" - - name: Test - run: dotnet test + - name: Test + run: dotnet test - - name: Build - run: dotnet publish RLBotCS -r win-x64 + - name: Build + run: dotnet publish RLBotCS -r win-x64 - - name: Upload build artifact - uses: actions/upload-artifact@v7 - with: - name: RLBotServer-windows - path: ./**/publish/RLBotServer.exe + - name: Upload build artifact + uses: actions/upload-artifact@v7 + with: + name: RLBotServer-windows + path: ./**/publish/RLBotServer.exe build-linux: runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v6 - with: - submodules: 'recursive' + - name: Checkout + uses: actions/checkout@v6 + with: + submodules: "recursive" + + - name: Test + run: dotnet test + + - name: Formatting + run: | + dotnet tool restore + dotnet csharpier check . + + - name: Build + run: dotnet publish RLBotCS -r linux-x64 + + - name: Upload build artifact + uses: actions/upload-artifact@v7 + with: + name: RLBotServer-ubuntu + path: ./**/publish/RLBotServer + + package-linux: + name: Package Linux + runs-on: ubuntu-latest + needs: [build-linux] + + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + submodules: "recursive" + fetch-depth: 0 - - name: Test - run: dotnet test + - uses: actions/download-artifact@v8 + with: + merge-multiple: "true" - - name: Formatting - run: | - dotnet tool restore - dotnet csharpier check . + - name: Install packaging dependencies + run: | + sudo apt-get update + sudo apt-get install -y rpm - - name: Build - run: dotnet publish RLBotCS -r linux-x64 + - name: Generate packages + run: | + chmod +x ./package-rlbot.sh + BIN_PATH="$(find . -type f -name RLBotServer | head -n 1)" + if [ -z "$BIN_PATH" ]; then + echo "RLBotServer binary not found in downloaded artifacts" >&2 + exit 1 + fi + chmod +x "$BIN_PATH" + OUTPUT_DIR=dist ./package-rlbot.sh "$BIN_PATH" - - name: Upload build artifact - uses: actions/upload-artifact@v7 - with: - name: RLBotServer-ubuntu - path: ./**/publish/RLBotServer + - name: Upload package artifacts + uses: actions/upload-artifact@v7 + with: + name: RLBotServer-linux-packages + path: dist/* release: name: Release runs-on: ubuntu-latest if: "startsWith(github.ref, 'refs/tags/')" - needs: [build-linux, build-windows] + needs: [build-linux, build-windows, package-linux] permissions: contents: write @@ -71,10 +109,14 @@ jobs: - uses: actions/download-artifact@v8 with: merge-multiple: "true" + - name: Publish to GitHub Releases uses: softprops/action-gh-release@v2 with: - files: ./RLBotCS/**/publish/RLBotServer* + files: | + ./RLBotCS/**/publish/RLBotServer* + ./*.deb + ./*.rpm generate_release_notes: true body: | Pre-built binaries that allows bots to interface with Rocket League via the RLBot v5 spec diff --git a/.gitignore b/.gitignore index 24abfb5..2d0ee8c 100644 --- a/.gitignore +++ b/.gitignore @@ -64,4 +64,7 @@ dlldata.c *.sln.iml # FlatBuffers -FlatBuffer/ \ No newline at end of file +FlatBuffer/ + +# Potential build artifacts +dist/ diff --git a/package-rlbot.sh b/package-rlbot.sh new file mode 100755 index 0000000..676625c --- /dev/null +++ b/package-rlbot.sh @@ -0,0 +1,244 @@ +#!/usr/bin/env bash +set -euo pipefail + +# package-rlbot.sh - Package RLBotServer binary for multiple Linux distributions +# +# Usage: ./package-rlbot.sh [distro_list] +# +# If no distribution list is provided, it defaults to packaging for ubuntu and fedora +# Supported distributions: ubuntu, fedora +# +# Examples: +# ./package-rlbot.sh ./RLBotServer ubuntu +# ./package-rlbot.sh ./RLBotServer # Defaults to ubuntu and fedora +# +# This script packages the RLBotServer binary for various Linux distributions, +# creating appropriate metadata and package structures. + +DEFAULT_DISTROS=("ubuntu" "fedora") + +usage() { + cat <<'EOF' +Usage: ./package-rlbot.sh [distro_list] + +If no distribution list is provided, it defaults to packaging all supported distributions. +Supported distributions: ubuntu, fedora. + +Examples: + ./package-rlbot.sh ./RLBotServer ubuntu + ./package-rlbot.sh ./RLBotServer # Defaults to ubuntu and fedora + +Environment overrides: + OUTPUT_DIR Output directory for packages (default: ./dist) +EOF +} + +require_cmd() { + if ! command -v "$1" >/dev/null 2>&1; then + echo "Missing required command: $1" >&2 + return 1 + fi +} + +get_git_version() { + require_cmd git + + local repo_root="$1" + local latest_tag + + latest_tag="$(git -C "$repo_root" describe --tags --abbrev=0 2>/dev/null || true)" + if [[ -z "$latest_tag" ]]; then + echo "Unable to determine version: no git tags found in $repo_root" >&2 + return 1 + fi + + echo "${latest_tag#v}" +} + +normalize_rpm_version() { + local input="$1" + + if [[ -z "$input" ]]; then + echo "RPM version is empty" >&2 + return 1 + fi + + echo "${input//-/.}" +} + +resolve_path() { + local input_path="$1" + local dir + dir="$(cd "$(dirname "$input_path")" && pwd)" + echo "${dir}/$(basename "$input_path")" +} + +ensure_x86_64() { + local arch_raw="$1" + case "$arch_raw" in + x86_64|amd64) + return 0 + ;; + *) + echo "Unsupported architecture: ${arch_raw}. Only x86-64 is supported." >&2 + return 1 + ;; + esac +} + +package_deb() { + require_cmd dpkg-deb + + local pkg_name="rlbotserver" + local pkg_dir="${tmp_root}/deb/${pkg_name}_${version}" + local deb_path="${out_dir}/${pkg_name}_${version}_${deb_arch}.deb" + + rm -rf "$pkg_dir" + mkdir -p "$pkg_dir/DEBIAN" "$pkg_dir/usr/bin" + + install -m 0755 "$binary_path" "$pkg_dir/usr/bin/$binary_name" + + cat > "$pkg_dir/DEBIAN/control" </dev/null + echo "Created ${deb_path}" +} + +package_rpm() { + require_cmd rpmbuild + require_cmd tar + + local pkg_name="rlbotserver" + local rpm_root="${tmp_root}/rpm" + local src_dir="${tmp_root}/${pkg_name}-${rpm_version}" + local spec_file="${rpm_root}/SPECS/${pkg_name}.spec" + + mkdir -p "${rpm_root}"/{BUILD,RPMS,SOURCES,SPECS,SRPMS} + rm -rf "$src_dir" + mkdir -p "$src_dir" + + install -m 0755 "$binary_path" "$src_dir/$binary_name" + tar -C "$tmp_root" -czf "$rpm_root/SOURCES/${pkg_name}-${rpm_version}.tar.gz" "${pkg_name}-${rpm_version}" + + cat > "$spec_file" </dev/null 2>&1 + + local rpm_file + rpm_file="$(find "$rpm_root/RPMS" -type f -name "*.rpm" | head -n 1)" + if [[ -z "$rpm_file" ]]; then + echo "RPM build failed: no package found in ${rpm_root}/RPMS" >&2 + return 1 + fi + + cp "$rpm_file" "$out_dir/" + echo "Created ${out_dir}/$(basename "$rpm_file")" +} + +main() { + if [[ ${1:-} == "-h" || ${1:-} == "--help" ]]; then + usage + exit 0 + fi + + if [[ $# -lt 1 ]]; then + usage + exit 1 + fi + + binary_path="$1" + shift + + script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + + if [[ $# -eq 0 ]]; then + distros=("${DEFAULT_DISTROS[@]}") + else + if [[ $# -eq 1 && "$1" == *","* ]]; then + IFS=',' read -r -a distros <<< "$1" + else + distros=("$@") + fi + fi + + binary_path="$(resolve_path "$binary_path")" + if [[ ! -f "$binary_path" ]]; then + echo "Binary not found: $binary_path" >&2 + exit 1 + fi + if [[ ! -x "$binary_path" ]]; then + echo "Binary is not executable: $binary_path" >&2 + exit 1 + fi + + binary_name="$(basename "$binary_path")" + + version="$(get_git_version "$script_dir")" + rpm_version="$(normalize_rpm_version "$version")" + out_dir="${OUTPUT_DIR:-dist}" + mkdir -p "$out_dir" + + arch_raw="$(uname -m)" + ensure_x86_64 "$arch_raw" + deb_arch="amd64" + rpm_arch="x86_64" + + tmp_root="$(mktemp -d)" + trap 'rm -rf "$tmp_root"' EXIT + + for distro in "${distros[@]}"; do + distro="${distro,,}" + case "$distro" in + ubuntu) + package_deb + ;; + fedora) + package_rpm + ;; + *) + echo "Unknown distribution: $distro" >&2 + echo "Supported distributions: ubuntu, fedora" >&2 + exit 1 + ;; + esac + done +} + +main "$@"