Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,16 @@ jobs:
name: plugin-wheel
path: dist/

- name: Verify Python API wheel artifacts
run: |
set -euo pipefail
shopt -s nullglob
wheels=(dist/nemo_relay-*.whl)
if [ "${#wheels[@]}" -ne 7 ]; then
echo "Error: expected 7 Python API wheel artifacts, found ${#wheels[@]}" >&2
Comment thread
willkill07 marked this conversation as resolved.
exit 1
fi

- name: Publish to PyPI with trusted publishing
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
with:
Expand Down
103 changes: 100 additions & 3 deletions .github/workflows/ci_node.yml
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,90 @@ jobs:
path: ${{ env.NEMO_RELAY_CI_WORKSPACE_TMP }}/npm/*.tgz
if-no-files-found: error

PackageMusllinux:
name: Package (musllinux-${{ matrix.node_arch }})
needs: [Test]
if: ${{ inputs.run_package && !cancelled() && needs.Test.result == 'success' }}
runs-on: ${{ matrix.runner }}
timeout-minutes: 60
permissions:
contents: read
strategy:
fail-fast: false
matrix:
include:
- node_arch: x64
runner: ubuntu-latest
container_image: node:24-alpine3.22@sha256:191c9f0080fcbbc6547a85dc0ff7988072214a355aabdc1d2ec55a7dae5eea8a
- node_arch: arm64
runner: ubuntu-24.04-arm
container_image: node:24-alpine3.22@sha256:191c9f0080fcbbc6547a85dc0ff7988072214a355aabdc1d2ec55a7dae5eea8a

steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
Comment thread
willkill07 marked this conversation as resolved.

- name: Load CI tool versions
id: ci-config
uses: ./.github/actions/load-ci-tool-versions

- name: Derive Node package version
working-directory: ${{ env.NEMO_RELAY_CI_WORKSPACE }}
run: |
set -euo pipefail
version="$(node -e 'const fs = require("fs"); const pkg = JSON.parse(fs.readFileSync("crates/node/package.json", "utf8")); if (!pkg.version) { throw new Error("crates/node/package.json missing version field"); } console.log(pkg.version);')"
if [[ "${{ inputs.ref_type }}" == "tag" ]]; then
version="${{ inputs.ref_name }}"
else
version="${version}+${GITHUB_SHA::8}"
fi
printf 'NEMO_RELAY_NODE_PACKAGE_VERSION=%s\n' "$version" >> "$GITHUB_ENV"

- name: Build and smoke-test musllinux Node package
working-directory: ${{ env.NEMO_RELAY_CI_WORKSPACE }}
run: |
set -euo pipefail
output_dir="${NEMO_RELAY_CI_WORKSPACE_TMP}/npm-musllinux"
mkdir -p "$output_dir"
docker run --rm \
--volume "${NEMO_RELAY_CI_WORKSPACE}:${NEMO_RELAY_CI_WORKSPACE}" \
--workdir "${NEMO_RELAY_CI_WORKSPACE}" \
--env NEMO_RELAY_RUST_VERSION="${{ steps.ci-config.outputs.rust_version }}" \
--env NEMO_RELAY_NODE_OUTPUT_DIR="$output_dir" \
--env NEMO_RELAY_NODE_VERSION="${NEMO_RELAY_NODE_PACKAGE_VERSION}" \
"${{ matrix.container_image }}" \
sh -ceu '
apk add --no-cache build-base curl
curl --proto "=https" --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain "$NEMO_RELAY_RUST_VERSION"
export PATH="$HOME/.cargo/bin:$PATH"
node scripts/package_node_musllinux.mjs --version "$NEMO_RELAY_NODE_VERSION" --out "$NEMO_RELAY_NODE_OUTPUT_DIR"
package="$(find "$NEMO_RELAY_NODE_OUTPUT_DIR" -maxdepth 1 -name "*.tgz" -print -quit)"
test -n "$package"
smoke_dir="$(mktemp -d)"
trap "rm -rf $smoke_dir" EXIT
cd "$smoke_dir"
npm install --ignore-scripts "$package"
node -e "require(\"nemo-relay-node\")"
'
Comment thread
coderabbitai[bot] marked this conversation as resolved.

- name: Verify musllinux N-API binary
run: |
set -euo pipefail
shopt -s nullglob
packages=("${{ env.NEMO_RELAY_CI_WORKSPACE_TMP }}/npm-musllinux"/*.tgz)
if [ "${#packages[@]}" -ne 1 ]; then
echo "Error: expected one npm package artifact, found ${#packages[@]}" >&2
exit 1
fi
tar tzf "${packages[0]}" | grep -Fx "package/nemo-relay.linux-${{ matrix.node_arch }}-musl.node"

- name: Upload musllinux npm package artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: npm-linux-musl-${{ matrix.node_arch == 'x64' && 'amd64' || 'arm64' }}
path: ${{ env.NEMO_RELAY_CI_WORKSPACE_TMP }}/npm-musllinux/*.tgz
if-no-files-found: error

PackageOpenClaw:
name: Package OpenClaw plugin
needs: [Test]
Expand Down Expand Up @@ -284,8 +368,8 @@ jobs:

Consolidate:
name: Consolidate
needs: [Package]
if: ${{ inputs.run_package && !cancelled() && needs.Package.result == 'success' }}
needs: [Package, PackageMusllinux]
if: ${{ inputs.run_package && !cancelled() && needs.Package.result == 'success' && needs.PackageMusllinux.result == 'success' }}
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
Expand All @@ -303,6 +387,18 @@ jobs:
name: npm-linux-arm64
path: npm-linux-arm64/

- name: Download npm linux-musl-amd64 artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: npm-linux-musl-amd64
path: npm-linux-musl-amd64/

- name: Download npm linux-musl-arm64 artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: npm-linux-musl-arm64
path: npm-linux-musl-arm64/

- name: Download npm macos-arm64 artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
Expand All @@ -326,7 +422,8 @@ jobs:
set -euo pipefail
mkdir -p combined
first=true
for dir in npm-linux-amd64 npm-linux-arm64 npm-macos-arm64 npm-windows-amd64 npm-windows-arm64; do
dirs=(npm-linux-amd64 npm-linux-arm64 npm-linux-musl-amd64 npm-linux-musl-arm64 npm-macos-arm64 npm-windows-amd64 npm-windows-arm64)
for dir in "${dirs[@]}"; do
shopt -s nullglob
tgzs=("${dir}"/*.tgz)
shopt -u nullglob
Expand Down
119 changes: 119 additions & 0 deletions .github/workflows/ci_python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,125 @@ jobs:
working-directory: ${{ env.NEMO_RELAY_CI_WORKSPACE }}
run: uv cache prune --ci

Package-musllinux:
name: Package (musllinux-${{ matrix.architecture }})
needs: [Test]
if: >-
${{
inputs.run_package &&
!cancelled() &&
needs.Test.result == 'success'
}}
runs-on: ${{ matrix.runner }}
timeout-minutes: 45
permissions:
contents: read
strategy:
fail-fast: false
matrix:
include:
- architecture: x86_64
runner: ubuntu-latest
image: quay.io/pypa/musllinux_1_2_x86_64@sha256:7b54360b191356838f6e41d4b9820c69be4f3d8dc8e7bca26f2684d48e5c8633
- architecture: aarch64
runner: ubuntu-24.04-arm
image: quay.io/pypa/musllinux_1_2_aarch64@sha256:274a947b4d5d745b56b868b0d052641e0798a652e3e65755987edf60547d1268

steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

- name: Load CI tool versions
id: ci-config
uses: ./.github/actions/load-ci-tool-versions

- name: Derive packaging tool versions
run: |
set -euo pipefail
maturin_version="$(awk '
$0 == "name = \"maturin\"" { found = 1; next }
found && /^version = / {
gsub(/"/, "", $3)
print $3
exit
}
' uv.lock)"
if [[ -z "$maturin_version" ]]; then
echo "Error: failed to read maturin version from uv.lock" >&2
exit 1
fi
version="$(sed -n 's/^version = "\(.*\)"$/\1/p' Cargo.toml | head -n1)"
if [[ -z "$version" ]]; then
echo "Error: failed to read workspace version from Cargo.toml" >&2
exit 1
fi
if [[ "${{ inputs.ref_type }}" == "tag" ]]; then
version="${{ inputs.ref_name }}"
else
version="${version}+${GITHUB_SHA::8}"
fi
{
printf 'NEMO_RELAY_MATURIN_VERSION=%s\n' "$maturin_version"
printf 'NEMO_RELAY_PACKAGE_VERSION=%s\n' "$version"
} >> "$GITHUB_ENV"

- name: Create packaging output directory
run: |
set -euo pipefail
mkdir -p "${{ env.NEMO_RELAY_CI_WORKSPACE_TMP }}/musllinux-wheels"

- name: Build and smoke test musllinux wheel
env:
MUSLLINUX_IMAGE: ${{ matrix.image }}
MUSLLINUX_INTERPRETER: /opt/python/cp311-cp311/bin/python
MUSLLINUX_WHEEL_DIRECTORY: ${{ env.NEMO_RELAY_CI_WORKSPACE_TMP }}/musllinux-wheels
NEMO_RELAY_RUST_VERSION: ${{ steps.ci-config.outputs.rust_version }}
run: |
set -euo pipefail
docker run --rm \
--volume "${{ env.NEMO_RELAY_CI_WORKSPACE }}:${{ env.NEMO_RELAY_CI_WORKSPACE }}" \
--workdir "${{ env.NEMO_RELAY_CI_WORKSPACE }}" \
--env MUSLLINUX_INTERPRETER \
--env MUSLLINUX_WHEEL_DIRECTORY \
--env NEMO_RELAY_MATURIN_VERSION \
--env NEMO_RELAY_PACKAGE_VERSION \
--env NEMO_RELAY_RUST_VERSION \
"$MUSLLINUX_IMAGE" \
/bin/sh -ec '
apk add --no-cache curl
curl --proto "=https" --tlsv1.2 --silent --show-error --fail https://sh.rustup.rs | \
sh -s -- -y --profile minimal --default-toolchain "$NEMO_RELAY_RUST_VERSION"
export PATH="$HOME/.cargo/bin:$PATH"
"$MUSLLINUX_INTERPRETER" -m pip install --no-cache-dir "maturin==$NEMO_RELAY_MATURIN_VERSION"
export PATH="$(dirname "$MUSLLINUX_INTERPRETER"):$PATH"
"$MUSLLINUX_INTERPRETER" scripts/package_python_musllinux.py \
--version "$NEMO_RELAY_PACKAGE_VERSION" \
--out "$MUSLLINUX_WHEEL_DIRECTORY" \
--interpreter "$MUSLLINUX_INTERPRETER"
wheel="$(find "$MUSLLINUX_WHEEL_DIRECTORY" -maxdepth 1 -name "*.whl" -print -quit)"
test -n "$wheel"
"$MUSLLINUX_INTERPRETER" -m venv /tmp/nemo-relay-musllinux-venv
/tmp/nemo-relay-musllinux-venv/bin/python -m pip install --no-deps "$wheel"
/tmp/nemo-relay-musllinux-venv/bin/python -c "import nemo_relay"
'

- name: Verify wheel platform tag
run: |
set -euo pipefail
shopt -s nullglob
wheels=("${{ env.NEMO_RELAY_CI_WORKSPACE_TMP }}/musllinux-wheels"/*-cp311-abi3-musllinux_1_2_${{ matrix.architecture }}.whl)
if [ "${#wheels[@]}" -ne 1 ]; then
echo "Error: expected one cp311-abi3 musllinux wheel for ${{ matrix.architecture }}, found ${#wheels[@]}" >&2
exit 1
fi

- name: Upload wheel artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: wheel-musllinux-${{ matrix.architecture }}
path: ${{ env.NEMO_RELAY_CI_WORKSPACE_TMP }}/musllinux-wheels/*.whl
if-no-files-found: error

Package:
name: Package (${{ matrix.platform }})
needs: [Test]
Expand Down
16 changes: 16 additions & 0 deletions .github/workflows/ci_rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -199,18 +199,23 @@ jobs:
- platform: linux-amd64
runner: ubuntu-latest
target: x86_64-unknown-linux-musl
musllinux_image: quay.io/pypa/musllinux_1_2_x86_64@sha256:7b54360b191356838f6e41d4b9820c69be4f3d8dc8e7bca26f2684d48e5c8633
- platform: linux-arm64
runner: ubuntu-24.04-arm
target: aarch64-unknown-linux-musl
musllinux_image: quay.io/pypa/musllinux_1_2_aarch64@sha256:274a947b4d5d745b56b868b0d052641e0798a652e3e65755987edf60547d1268
- platform: macos-arm64
runner: macos-15
target: aarch64-apple-darwin
musllinux_image: ''
- platform: windows-amd64
runner: windows-2022
target: x86_64-pc-windows-msvc
musllinux_image: ''
- platform: windows-arm64
runner: windows-11-arm
target: aarch64-pc-windows-msvc
musllinux_image: ''

steps:
- name: Checkout
Expand Down Expand Up @@ -254,6 +259,17 @@ jobs:
set -e
cargo build --release --target "${{ matrix.target }}" -p nemo-relay-cli

- name: Verify CLI binary on musllinux
if: ${{ matrix.musllinux_image != '' }}
working-directory: ${{ env.NEMO_RELAY_CI_WORKSPACE }}
run: |
set -euo pipefail
docker run --rm \
--volume "${{ env.NEMO_RELAY_CI_WORKSPACE }}:${{ env.NEMO_RELAY_CI_WORKSPACE }}" \
--workdir "${{ env.NEMO_RELAY_CI_WORKSPACE }}" \
"${{ matrix.musllinux_image }}" \
"${{ env.NEMO_RELAY_CI_WORKSPACE }}/target/${{ matrix.target }}/release/nemo-relay" --version

- name: Stage CLI binary artifact
working-directory: ${{ env.NEMO_RELAY_CI_WORKSPACE }}
run: |
Expand Down
11 changes: 7 additions & 4 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,12 +239,15 @@ The release pipeline then:
validation.
3. Builds publishable package artifacts with the exact tag version:
- `package-rust` packs the published Rust crates for local validation.
- `package-node` packs the npm Node.js package.
- `package-node` packs the npm Node.js package with GNU and musl Linux
N-API binaries for x86_64 and ARM64.
- `package-openclaw` packs the npm OpenClaw plugin package.
- `package-python` builds platform `nemo-relay` wheels.
- `package-python` builds platform `nemo-relay` wheels, including
`musllinux_1_2_x86_64` and `musllinux_1_2_aarch64` wheels.
- `package-python-plugin` builds the `nemo-relay-plugin` wheel.
- The CLI release-asset job uploads each platform `nemo-relay` binary and
includes those binaries in `SHA256SUMS`.
- The CLI release-asset job uploads each platform `nemo-relay` binary,
validates the Linux binaries on musllinux, and includes those binaries in
`SHA256SUMS`.
4. Publishes packages from the top-level workflow after the reusable packaging
jobs complete:
- `publish-rust` stamps Cargo workspace versions from the release tag, then
Expand Down
Loading
Loading