From 6ac7885cfe8f3d744023d37fd1e3a07f1e2b15a2 Mon Sep 17 00:00:00 2001 From: Cyrus Gray <144336577+graycyrus@users.noreply.github.com> Date: Fri, 1 May 2026 14:19:24 +0530 Subject: [PATCH] ci: add dedicated staging release workflow (#1066) --- .github/workflows/release-staging.yml | 296 ++++++++++++++++++++++++++ 1 file changed, 296 insertions(+) create mode 100644 .github/workflows/release-staging.yml diff --git a/.github/workflows/release-staging.yml b/.github/workflows/release-staging.yml new file mode 100644 index 000000000..e7907b4bc --- /dev/null +++ b/.github/workflows/release-staging.yml @@ -0,0 +1,296 @@ +--- +name: Release (Staging) +on: + workflow_dispatch: {} +permissions: + contents: read + packages: read +concurrency: + group: release-staging + cancel-in-progress: false +# --------------------------------------------------------------------------- +# Job dependency graph +# +# prepare-build +# │ +# build-desktop (matrix: macOS arm64, macOS x64, linux, windows) +# --------------------------------------------------------------------------- +jobs: + # ========================================================================= + # Phase 1: Resolve build context (no version bump, no tag) + # ========================================================================= + prepare-build: + name: Prepare build context + runs-on: ubuntu-latest + outputs: + version: ${{ steps.resolve.outputs.version }} + sha: ${{ steps.resolve.outputs.sha }} + build_ref: ${{ steps.resolve.outputs.build_ref }} + base_url: ${{ steps.resolve.outputs.base_url }} + steps: + - name: Enforce staging branch + if: github.ref != 'refs/heads/staging' + run: | + echo "This workflow can only run from staging. Current ref: $GITHUB_REF" + exit 1 + - name: Checkout staging + uses: actions/checkout@v4 + with: + ref: staging + fetch-depth: 1 + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 24.x + - name: Resolve build outputs + id: resolve + shell: bash + run: | + VERSION="$(node -p "require('./app/package.json').version")" + SHA="$(git rev-parse HEAD)" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "sha=$SHA" >> "$GITHUB_OUTPUT" + echo "build_ref=$SHA" >> "$GITHUB_OUTPUT" + echo "base_url=https://staging-api.tinyhumans.ai/" >> "$GITHUB_OUTPUT" + + # ========================================================================= + # Phase 2: Build desktop artifacts (parallel matrix, debug profile) + # ========================================================================= + build-desktop: + name: "Desktop: ${{ matrix.settings.artifact_suffix }}" + needs: [prepare-build] + runs-on: ${{ matrix.settings.platform }} + strategy: + fail-fast: false + matrix: + settings: + - platform: macos-latest + args: --target aarch64-apple-darwin + target: aarch64-apple-darwin + artifact_suffix: aarch64-apple-darwin + - platform: macos-latest + args: --target x86_64-apple-darwin + target: x86_64-apple-darwin + artifact_suffix: x86_64-apple-darwin + - platform: ubuntu-22.04 + args: --target x86_64-unknown-linux-gnu --bundles deb + target: x86_64-unknown-linux-gnu + artifact_suffix: ubuntu + - platform: windows-latest + args: --target x86_64-pc-windows-msvc + target: x86_64-pc-windows-msvc + artifact_suffix: windows + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + OPENHUMAN_APP_ENV: staging + VITE_OPENHUMAN_APP_ENV: staging + OPENHUMAN_TELEGRAM_BOT_USERNAME: alphahumantest_bot + VITE_TELEGRAM_BOT_USERNAME: alphahumantest_bot + steps: + - name: Checkout build ref + uses: actions/checkout@v4 + with: + ref: ${{ needs.prepare-build.outputs.build_ref }} + fetch-depth: 1 + submodules: recursive + - name: Set Xcode version + if: matrix.settings.platform == 'macos-latest' + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: latest-stable + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + cache: true + - name: Setup Node.js 24.x + uses: actions/setup-node@v4 + with: + node-version: 24.x + - name: Install Rust (rust-toolchain.toml) + uses: dtolnay/rust-toolchain@1.93.0 + with: + targets: ${{ matrix.settings.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }} + - name: Install Tauri dependencies (ubuntu only) + if: matrix.settings.platform == 'ubuntu-22.04' + run: | + sudo apt-get update + sudo apt-get install -y \ + libgtk-3-dev libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev \ + patchelf cmake libasound2-dev libxdo-dev libxtst-dev libx11-dev libxi-dev \ + libevdev-dev libssl-dev libclang-dev \ + libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 \ + libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 \ + libgbm1 libpango-1.0-0 libcairo2 libatspi2.0-0 libxshmfence1 libu2f-udev + - name: Dump missing shared libs (debug) + if: matrix.settings.platform == 'ubuntu-22.04' + shell: bash + run: | + set +e + for BIN in \ + app/src-tauri/target/${{ matrix.settings.target }}/debug/OpenHuman \ + target/${{ matrix.settings.target }}/debug/OpenHuman; do + if [ -x "$BIN" ]; then + echo "ldd $BIN" + ldd "$BIN" | grep 'not found' || echo " all resolved" + fi + done + + # Skip first 7 lines of Cargo.lock (workspace package version bumps) so the key tracks dependency changes only + - name: Cargo.lock fingerprint (deps only) + id: cargo-lock-fingerprint + shell: bash + run: | + echo "hash=$(tail -n +8 Cargo.lock | openssl dgst -sha256 | awk '{print $2}')" >> "$GITHUB_OUTPUT" + - name: Cache Cargo registry and git sources + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: + ${{ runner.os }}-cargo-registry-${{ steps.cargo-lock-fingerprint.outputs.hash + }} + restore-keys: | + ${{ runner.os }}-cargo-registry- + + - name: Cache CEF binary distribution (unix) + if: matrix.settings.platform != 'windows-latest' + uses: actions/cache@v4 + with: + path: | + ~/Library/Caches/tauri-cef + ~/.cache/tauri-cef + key: + cef-${{ matrix.settings.target }}-${{ hashFiles('app/src-tauri/Cargo.toml') + }} + restore-keys: | + cef-${{ matrix.settings.target }}- + - name: Cache CEF binary distribution (windows) + if: matrix.settings.platform == 'windows-latest' + uses: actions/cache@v4 + with: + path: ~/AppData/Local/tauri-cef + key: + cef-${{ matrix.settings.target }}-${{ hashFiles('app/src-tauri/Cargo.toml') + }} + restore-keys: | + cef-${{ matrix.settings.target }}- + + - name: Cache vendored tauri-cli binary (unix) + if: matrix.settings.platform != 'windows-latest' + id: tauri-cli-cache-unix + uses: actions/cache@v4 + with: + path: ~/.cargo/bin/cargo-tauri + key: + vendored-tauri-cli-${{ runner.os }}-${{ hashFiles('app/src-tauri/vendor/tauri-cef/crates/tauri-cli/Cargo.toml') + }} + - name: Cache vendored tauri-cli binary (windows) + if: matrix.settings.platform == 'windows-latest' + id: tauri-cli-cache-windows + uses: actions/cache@v4 + with: + path: ~/.cargo/bin/cargo-tauri.exe + key: + vendored-tauri-cli-${{ runner.os }}-${{ hashFiles('app/src-tauri/vendor/tauri-cef/crates/tauri-cli/Cargo.toml') + }} + - name: Install vendored tauri-cli (cef-aware bundler) + if: + (matrix.settings.platform == 'windows-latest' && steps.tauri-cli-cache-windows.outputs.cache-hit + != 'true') || (matrix.settings.platform != 'windows-latest' && steps.tauri-cli-cache-unix.outputs.cache-hit + != 'true') + shell: bash + run: cargo install --locked --path app/src-tauri/vendor/tauri-cef/crates/tauri-cli + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Define Tauri configuration overrides + id: config-overrides + uses: actions/github-script@v7 + env: + BASE_URL: ${{ needs.prepare-build.outputs.base_url }} + with: + script: | + const workspacePath = process.env.GITHUB_WORKSPACE.replace(/\\/g, '/'); + const prefix = workspacePath.startsWith('/') ? 'file://' : 'file:///'; + const moduleUrl = `${prefix}${workspacePath}/scripts/prepareTauriConfig.js`; + const { default: prepareTauriConfig } = await import(moduleUrl); + const config = prepareTauriConfig(); + core.setOutput('json', JSON.stringify(config)); + + - name: Resolve core manifest and binary names + id: core-paths + shell: bash + run: | + if [ -f "openhuman_core/Cargo.toml" ]; then + CORE_DIR="openhuman_core" + elif [ -f "rust-core/Cargo.toml" ]; then + CORE_DIR="rust-core" + elif [ -f "Cargo.toml" ] && grep -q '^name = "openhuman"' Cargo.toml; then + CORE_DIR="." + else + echo "No core Cargo manifest found (expected root Cargo.toml with openhuman, openhuman_core/Cargo.toml, or rust-core/Cargo.toml)" + exit 1 + fi + SIDE_CAR_BASE="$(node -e "const fs=require('fs');const c=JSON.parse(fs.readFileSync('app/src-tauri/tauri.conf.json','utf8'));const b=(c.bundle&&Array.isArray(c.bundle.externalBin)&&c.bundle.externalBin[0])||'binaries/openhuman-core';process.stdout.write(String(b).split('/').pop());")" + CORE_BIN_NAME="${SIDE_CAR_BASE}" + BUILD_PROFILE="debug" + echo "core_dir=$CORE_DIR" >> "$GITHUB_OUTPUT" + echo "core_manifest=$CORE_DIR/Cargo.toml" >> "$GITHUB_OUTPUT" + echo "core_target_dir=target/$MATRIX_TARGET/$BUILD_PROFILE" >> "$GITHUB_OUTPUT" + echo "build_profile=$BUILD_PROFILE" >> "$GITHUB_OUTPUT" + echo "core_bin_name=$CORE_BIN_NAME" >> "$GITHUB_OUTPUT" + echo "sidecar_base=$SIDE_CAR_BASE" >> "$GITHUB_OUTPUT" + env: + MATRIX_TARGET: ${{ matrix.settings.target }} + - name: Build sidecar core binary + shell: bash + run: | + cargo build \ + --manifest-path "$CORE_MANIFEST" \ + --target "$MATRIX_TARGET" \ + --bin "$CORE_BIN_NAME" + env: + MATRIX_TARGET: ${{ matrix.settings.target }} + CORE_MANIFEST: ${{ steps.core-paths.outputs.core_manifest }} + CORE_BIN_NAME: ${{ steps.core-paths.outputs.core_bin_name }} + OPENHUMAN_APP_ENV: staging + - name: Stage sidecar for Tauri bundler + shell: bash + run: | + bash scripts/release/stage-sidecar.sh \ + "${{ matrix.settings.target }}" \ + "${{ steps.core-paths.outputs.core_target_dir }}" \ + "${{ steps.core-paths.outputs.core_bin_name }}" \ + "${{ steps.core-paths.outputs.sidecar_base }}" + + - name: Build and package Tauri app (CEF, vendored CLI, debug) + shell: bash + working-directory: app + env: + BASE_URL: ${{ needs.prepare-build.outputs.base_url }} + VITE_BACKEND_URL: ${{ needs.prepare-build.outputs.base_url }} + VITE_DEBUG: ${{ vars.VITE_DEBUG }} + MACOSX_DEPLOYMENT_TARGET: ${{ matrix.settings.platform == 'macos-latest' && '10.15' || '' }} + TAURI_CONFIG_OVERRIDE: ${{ steps.config-overrides.outputs.json }} + MATRIX_ARGS: ${{ matrix.settings.args }} + run: | + NODE_OPTIONS="--max-old-space-size=8192" cargo tauri build --debug -c "$TAURI_CONFIG_OVERRIDE" $MATRIX_ARGS + + - name: Upload staging desktop bundles + uses: actions/upload-artifact@v4 + with: + name: + desktop-bundles-${{ matrix.settings.platform }}-${{ matrix.settings.artifact_suffix + }} + path: | + app/src-tauri/target/${{ matrix.settings.target }}/debug/bundle/** + target/${{ matrix.settings.target }}/debug/bundle/** + - name: Upload standalone CLI artifacts + uses: actions/upload-artifact@v4 + with: + name: + standalone-bins-${{ matrix.settings.platform }}-${{ matrix.settings.artifact_suffix + }} + path: | + ${{ steps.core-paths.outputs.core_target_dir }}/${{ steps.core-paths.outputs.core_bin_name }}${{ matrix.settings.platform == 'windows-latest' && '.exe' || '' }}