name: Release Unix

on:
  workflow_call:
    inputs:
      target:
        description: Release target label.
        required: true
        type: string
      runner:
        description: GitHub Actions runner label.
        required: true
        type: string
      os:
        description: Operating-system family (linux or darwin).
        required: true
        type: string
      arch:
        description: Release architecture label.
        required: true
        type: string
      agentd_artifact:
        description: Matching musl agentd artifact.
        required: true
        type: string
      firmware_artifact:
        description: Matching kernel/libkrunfw producer artifact.
        required: true
        type: string
      libkrunfw_file:
        description: Canonical runtime library filename.
        required: true
        type: string
      libkrunfw_asset:
        description: Public release asset name for libkrunfw.
        required: true
        type: string
      napi_target:
        description: napi-rs target triple.
        required: true
        type: string
      node_file:
        description: Platform Node native binding filename.
        required: true
        type: string
      npm_dir:
        description: Platform npm package directory.
        required: true
        type: string
      go_ffi_file:
        description: Cargo output filename for the Go FFI library.
        required: true
        type: string
      go_bundle_name:
        description: Embedded Go SDK bundle filename.
        required: true
        type: string

permissions:
  contents: read

env:
  CARGO_TERM_COLOR: always
  CARGO_NET_RETRY: "10"
  CARGO_HTTP_TIMEOUT: "120"
  CARGO_HTTP_MULTIPLEXING: "false"
  CARGO_INCREMENTAL: "0"
  LIBKRUNFW_VERSION: "5.6.1"
  LIBKRUNFW_ABI: "5"

# These jobs produce shipped artifacts. Keep every Cargo and maturin build on
# the workspace release profile (including its full LTO settings).
jobs:
  runtime:
    name: Runtime (${{ inputs.target }})
    runs-on: ${{ inputs.runner }}
    timeout-minutes: 60
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

      - uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable

      - uses: Swatinem/rust-cache@258712b0b7b1ddf8bddc9fc3b0faca682b2736c3 # v2
        with:
          cache-bin: false
          cache-targets: true
          shared-key: release-runtime-${{ inputs.target }}

      - name: Install build dependencies (Linux)
        if: inputs.os == 'linux'
        run: sudo apt-get update && sudo apt-get install -y libcap-ng-dev

      - name: Download agentd
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: ${{ inputs.agentd_artifact }}
          path: build/

      - name: Download firmware
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: ${{ inputs.firmware_artifact }}
          path: firmware/

      - name: Stage libkrunfw (Linux)
        if: inputs.os == 'linux'
        run: |
          mkdir -p build
          cp firmware/${{ inputs.libkrunfw_file }} build/
          cd build
          ln -sf ${{ inputs.libkrunfw_file }} libkrunfw.so.${{ env.LIBKRUNFW_ABI }}
          ln -sf libkrunfw.so.${{ env.LIBKRUNFW_ABI }} libkrunfw.so

      - name: Build libkrunfw (macOS)
        if: inputs.os == 'darwin'
        run: |
          mkdir -p build
          cc -fPIC -DABI_VERSION=${{ env.LIBKRUNFW_ABI }} -shared \
            -o build/${{ inputs.libkrunfw_file }} firmware/kernel.c
          ln -sf ${{ inputs.libkrunfw_file }} build/libkrunfw.dylib

      - name: Build msb
        run: |
          cargo build --release --no-default-features --features net,ssh -p microsandbox-cli
          cp target/release/msb build/msb

      - name: Codesign msb (macOS)
        if: inputs.os == 'darwin'
        run: codesign --entitlements msb-entitlements.plist --force -s - build/msb

      - name: Verify runtime binary
        run: build/msb --version

      - name: Upload runtime artifacts
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: runtime-${{ inputs.target }}
          # upload-artifact dereferences symlinks. Listing only the canonical
          # library prevents Linux SONAME aliases from becoming duplicate real
          # files that downstream packaging could mistake for the release name.
          path: |
            build/agentd
            build/msb
            build/${{ inputs.libkrunfw_file }}
          compression-level: 0

  metrics:
    name: Metrics (${{ inputs.target }})
    runs-on: ${{ inputs.runner }}
    timeout-minutes: 45
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

      - uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable

      - uses: Swatinem/rust-cache@258712b0b7b1ddf8bddc9fc3b0faca682b2736c3 # v2
        with:
          cache-bin: false
          cache-targets: true
          shared-key: release-metrics-${{ inputs.target }}

      - name: Build msb-metrics
        run: cargo build --release -p microsandbox-metrics-collector

      - name: Upload metrics artifact
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: metrics-${{ inputs.target }}
          path: target/release/msb-metrics
          compression-level: 0

  go-ffi:
    name: Go FFI (${{ inputs.target }})
    needs: runtime
    runs-on: ${{ inputs.runner }}
    timeout-minutes: 60
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

      - uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable

      - uses: Swatinem/rust-cache@258712b0b7b1ddf8bddc9fc3b0faca682b2736c3 # v2
        with:
          cache-bin: false
          cache-targets: true
          shared-key: release-go-${{ inputs.target }}

      - name: Install build dependencies (Linux)
        if: inputs.os == 'linux'
        run: sudo apt-get update && sudo apt-get install -y libcap-ng-dev

      - name: Download runtime artifacts
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: runtime-${{ inputs.target }}
          path: build/

      - name: Build Go FFI
        run: cargo build --release -p microsandbox-go

      - name: Upload Go FFI artifact
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: go-ffi-${{ inputs.target }}
          path: target/release/${{ inputs.go_ffi_file }}
          compression-level: 0

  node:
    name: Node SDK (${{ inputs.target }})
    needs: runtime
    runs-on: ${{ inputs.runner }}
    timeout-minutes: 60
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

      - uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable

      - uses: Swatinem/rust-cache@258712b0b7b1ddf8bddc9fc3b0faca682b2736c3 # v2
        with:
          cache-bin: false
          cache-targets: true
          shared-key: release-node-${{ inputs.target }}

      - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
        with:
          node-version: 22
          cache: npm
          cache-dependency-path: sdk/node-ts/package-lock.json

      - name: Install build dependencies (Linux)
        if: inputs.os == 'linux'
        run: sudo apt-get update && sudo apt-get install -y libcap-ng-dev

      - name: Download runtime artifacts
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: runtime-${{ inputs.target }}
          path: build/

      - name: Build Node native binding
        working-directory: sdk/node-ts
        run: |
          node scripts/prune-platform-optional-deps.mjs
          npm install --package-lock=false
          npm run build:native -- --target ${{ inputs.napi_target }}
          node -e 'require("./native/index.cjs")'

      - name: Prepare Node platform package
        working-directory: sdk/node-ts
        run: node scripts/prepare-platform-package.mjs ${{ inputs.npm_dir }}

      - name: Upload Node SDK artifacts
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: node-sdk-${{ inputs.npm_dir }}
          path: |
            sdk/node-ts/native/${{ inputs.node_file }}
            sdk/node-ts/native/index.cjs
            sdk/node-ts/native/index.d.ts
            sdk/node-ts/npm/${{ inputs.npm_dir }}/${{ inputs.node_file }}
            sdk/node-ts/npm/${{ inputs.npm_dir }}/bin/msb
            sdk/node-ts/npm/${{ inputs.npm_dir }}/lib/*
          compression-level: 0

  python:
    name: Python SDK (${{ inputs.target }})
    needs: runtime
    runs-on: ${{ inputs.runner }}
    timeout-minutes: 60
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

      - uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable

      - uses: Swatinem/rust-cache@258712b0b7b1ddf8bddc9fc3b0faca682b2736c3 # v2
        with:
          cache-bin: false
          cache-targets: true
          shared-key: release-python-${{ inputs.target }}

      - name: Install build dependencies (Linux)
        if: inputs.os == 'linux'
        run: sudo apt-get update && sudo apt-get install -y libcap-ng-dev

      - name: Download runtime artifacts
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: runtime-${{ inputs.target }}
          path: build/

      - name: Stage runtime bundle
        run: |
          mkdir -p sdk/python/microsandbox/_bundled/bin sdk/python/microsandbox/_bundled/lib
          cp build/msb sdk/python/microsandbox/_bundled/bin/
          cp build/${{ inputs.libkrunfw_file }} sdk/python/microsandbox/_bundled/lib/

      - name: Build Python wheel
        uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1
        with:
          working-directory: sdk/python
          command: build
          args: --release --out dist
          manylinux: ${{ inputs.os == 'linux' && '2_28' || 'off' }}
          before-script-linux: dnf install -y libcap-ng-devel

      - name: Upload Python SDK wheel
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: python-sdk-${{ inputs.target }}
          path: sdk/python/dist/*.whl
          compression-level: 0

  assemble-platform:
    name: Assemble (${{ inputs.target }})
    needs: [runtime, metrics, go-ffi]
    runs-on: ubuntu-latest
    steps:
      - name: Download runtime artifacts
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: runtime-${{ inputs.target }}
          path: bundle/runtime/

      - name: Download metrics artifact
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: metrics-${{ inputs.target }}
          path: bundle/metrics/

      - name: Download Go FFI artifact
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: go-ffi-${{ inputs.target }}
          path: bundle/go/

      - name: Stage platform artifacts
        run: |
          set -euo pipefail
          mkdir -p artifacts
          cp bundle/runtime/msb artifacts/msb-${{ inputs.target }}
          cp bundle/metrics/msb-metrics artifacts/msb-metrics-${{ inputs.target }}
          cp bundle/runtime/${{ inputs.libkrunfw_file }} artifacts/${{ inputs.libkrunfw_asset }}
          if [ "${{ inputs.os }}" = "linux" ]; then
            cp bundle/runtime/agentd artifacts/agentd-${{ inputs.arch }}
          fi
          tar -czf artifacts/microsandbox-${{ inputs.target }}.tar.gz \
            -C bundle/runtime msb ${{ inputs.libkrunfw_file }}
          cp bundle/go/${{ inputs.go_ffi_file }} artifacts/${{ inputs.go_bundle_name }}

      - name: Upload platform artifacts
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: release-${{ inputs.target }}
          path: artifacts/
          compression-level: 0
