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
16 changes: 1 addition & 15 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -197,17 +197,7 @@ jobs:
ref_type: ${{ github.ref_type }}
ref_name: ${{ github.ref_name }}
run_package: ${{ needs.ci_changes.outputs.run_node_package == 'true' }}

ci_openclaw:
name: OpenClaw
needs: [prepare, ci_changes, ci_check]
uses: ./.github/workflows/ci_openclaw.yml
if: ${{ needs.ci_check.result == 'success' && needs.ci_changes.outputs.run_openclaw == 'true' }}
permissions:
contents: read
with:
ref_type: ${{ github.ref_type }}
ref_name: ${{ github.ref_name }}
run_openclaw: ${{ needs.ci_changes.outputs.run_openclaw == 'true' }}

ci_python:
name: Python
Expand Down Expand Up @@ -247,7 +237,6 @@ jobs:
- ci_rust
- ci_go
- ci_node
- ci_openclaw
- ci_python
- ci_wasm
if: ${{ always() && !cancelled() && needs.prepare.result == 'success' && ! fromJSON(needs.prepare.outputs.has_skip_ci_label) }}
Expand All @@ -263,7 +252,6 @@ jobs:
RUST_RESULT: ${{ needs.ci_rust.result }}
GO_RESULT: ${{ needs.ci_go.result }}
NODE_RESULT: ${{ needs.ci_node.result }}
OPENCLAW_RESULT: ${{ needs.ci_openclaw.result }}
PYTHON_RESULT: ${{ needs.ci_python.result }}
WEBASSEMBLY_RESULT: ${{ needs.ci_wasm.result }}
publish_docs: ${{ needs.prepare.outputs.publish_docs }}
Expand Down Expand Up @@ -302,13 +290,11 @@ jobs:
if [[ "$publish_packages" == "true" ]]; then
require_success "Rust" "$RUST_RESULT"
require_success "Node.js" "$NODE_RESULT"
require_success "OpenClaw" "$OPENCLAW_RESULT"
require_success "Python" "$PYTHON_RESULT"
require_success "WebAssembly" "$WEBASSEMBLY_RESULT"
else
allow_success_or_skipped "Rust" "$RUST_RESULT"
allow_success_or_skipped "Node.js" "$NODE_RESULT"
allow_success_or_skipped "OpenClaw" "$OPENCLAW_RESULT"
allow_success_or_skipped "Python" "$PYTHON_RESULT"
allow_success_or_skipped "WebAssembly" "$WEBASSEMBLY_RESULT"
fi
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ci_changes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,9 @@ jobs:
run_dependencies: ${{ inputs.full_ci || steps.filter.outputs.dependencies == 'true' }}
run_docs: ${{ (inputs.full_ci || startsWith(inputs.ref_name, 'pull-request/')) && (inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.shared == 'true' || steps.filter.outputs.docs == 'true') }}
run_go: ${{ inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.shared == 'true' || steps.filter.outputs.go == 'true' }}
run_node: ${{ inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.shared == 'true' || steps.filter.outputs.node == 'true' }}
run_node: ${{ inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.shared == 'true' || steps.filter.outputs.node == 'true' || steps.filter.outputs.openclaw == 'true' }}
run_node_package: ${{ inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.node_package == 'true' }}
run_openclaw: ${{ inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.shared == 'true' || steps.filter.outputs.openclaw == 'true' }}
run_openclaw: ${{ inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.shared == 'true' || steps.filter.outputs.node == 'true' || steps.filter.outputs.openclaw == 'true' }}
run_python: ${{ inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.shared == 'true' || steps.filter.outputs.python == 'true' }}
run_python_package: ${{ inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.python_package == 'true' }}
run_rust: ${{ inputs.full_ci || steps.filter.outputs.ci == 'true' || steps.filter.outputs.shared == 'true' || steps.filter.outputs.rust == 'true' }}
Expand Down
83 changes: 82 additions & 1 deletion .github/workflows/ci_node.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ on:
required: false
default: true
type: boolean
run_openclaw:
description: 'Whether to run OpenClaw integration checks'
required: false
default: false
type: boolean
secrets:
CODECOV_TOKEN:
required: false
Expand Down Expand Up @@ -90,6 +95,11 @@ jobs:
working-directory: ${{ env.NEMO_FLOW_CI_WORKSPACE }}
run: just --set ci true --set output_dir "${{ github.workspace }}" test-node

- name: Run OpenClaw integration checks
if: ${{ inputs.run_openclaw }}
working-directory: ${{ env.NEMO_FLOW_CI_WORKSPACE }}
run: just --set ci true test-openclaw

- name: Upload Node coverage to Codecov
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6
if: ${{ !startsWith(matrix.platform, 'windows') }}
Expand Down Expand Up @@ -167,11 +177,13 @@ jobs:
tool: just@${{ steps.ci-config.outputs.just_version }}

- name: Create packaging output directory
if: ${{ inputs.run_package }}
run: |
set -e
mkdir -p "${{ env.NEMO_FLOW_CI_WORKSPACE_TMP }}/npm"

- name: Derive Node package version
if: ${{ inputs.run_package }}
working-directory: ${{ env.NEMO_FLOW_CI_WORKSPACE }}
run: |
set -e
Expand All @@ -185,6 +197,7 @@ jobs:
printf 'NEMO_FLOW_PACKAGE_VERSION=%s\n' "$version" >> "$GITHUB_ENV"

- name: Package Node
if: ${{ inputs.run_package }}
working-directory: ${{ env.NEMO_FLOW_CI_WORKSPACE }}
run: |
set -e
Expand All @@ -196,15 +209,83 @@ jobs:

- name: Upload npm package artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: ${{ inputs.run_package }}
with:
name: npm-${{ matrix.platform }}
path: ${{ env.NEMO_FLOW_CI_WORKSPACE_TMP }}/npm/*.tgz
if-no-files-found: error

PackageOpenClaw:
name: Package OpenClaw plugin
needs: [Test]
if: ${{ inputs.run_openclaw && !cancelled() && needs.Test.result == 'success' }}
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

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

- uses: actions-rust-lang/setup-rust-toolchain@150fca883cd4034361b621bd4e6a9d34e5143606 # v1.15.4
with:
cache: false
toolchain: ${{ steps.ci-config.outputs.rust_version }}

- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
shared-key: nemo-flow-rust-openclaw-package-${{ runner.os }}-${{ runner.arch }}-${{ steps.ci-config.outputs.rust_version }}
workspaces: . -> target
cache-all-crates: true
cache-bin: false
save-if: false

- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
with:
node-version: ${{ steps.ci-config.outputs.node_version }}

- uses: taiki-e/install-action@5939f3337e40968c39aa70f5ecb1417a92fb25a0 # v2.75.15
with:
tool: just@${{ steps.ci-config.outputs.just_version }}

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

- name: Package OpenClaw plugin
working-directory: ${{ env.NEMO_FLOW_CI_WORKSPACE }}
run: |
set -euo pipefail
just \
--set ci true \
--set output_dir "${NEMO_FLOW_CI_WORKSPACE_TMP}" \
--set ref_name "${NEMO_FLOW_OPENCLAW_PACKAGE_VERSION}" \
package-openclaw

- name: Upload OpenClaw plugin artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: openclaw-npm
path: ${{ env.NEMO_FLOW_CI_WORKSPACE_TMP }}/openclaw/*.tgz
if-no-files-found: error

Consolidate:
name: Consolidate
needs: [Package]
if: ${{ !cancelled() && needs.Package.result == 'success' }}
if: ${{ inputs.run_package && !cancelled() && needs.Package.result == 'success' }}
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
Expand Down
97 changes: 0 additions & 97 deletions .github/workflows/ci_openclaw.yml

This file was deleted.

5 changes: 4 additions & 1 deletion docs/contribute/testing-and-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ just ci=true test-rust
just test-python
just test-go
just test-node
just test-openclaw
just test-wasm
```

Expand Down Expand Up @@ -62,11 +63,13 @@ uv run pytest
### Node.js

Run the Node.js validation loop when a change touches the NAPI binding or
JavaScript package surface.
JavaScript package surface. Run the OpenClaw target when Node changes can affect
the OpenClaw plugin or when touching `integrations/openclaw`.

```bash
npm install --ignore-scripts
npm test --workspace=nemo-flow-node
just test-openclaw
```

## Documentation Checklist
Expand Down
5 changes: 3 additions & 2 deletions integrations/openclaw/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,9 @@ npm run pack:check --workspace=nemo-flow-openclaw

`npm run build --workspace=nemo-flow-openclaw` emits production files under
`integrations/openclaw/dist/`. Tests compile to
`integrations/openclaw/.test-dist/` so test artifacts do not enter the
installable package.
`integrations/openclaw/.test-dist/` from the sibling
`integrations/openclaw/test/` directory so test artifacts do not enter the
installable package or production source tree.

The optional live smoke test requires a working installed `nemo-flow-node`
binding:
Expand Down
4 changes: 2 additions & 2 deletions integrations/openclaw/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@
"pack:check": "node scripts/check-pack-payload.mjs",
"prepack": "npm run build",
"typecheck": "tsc -p tsconfig.json --noEmit",
"test": "npm run build:test && node --test \".test-dist/src/__tests__/*.test.js\"",
"test:live": "npm run build:test && NEMO_FLOW_OPENCLAW_LIVE_SMOKE=1 node --test \".test-dist/src/__tests__/live-smoke.test.js\""
"test": "npm run build:test && node --test \".test-dist/test/*.test.js\"",
"test:live": "npm run build:test && node scripts/test-live.mjs"
},
"peerDependencies": {
"openclaw": ">=2026.5.6"
Expand Down
4 changes: 2 additions & 2 deletions integrations/openclaw/scripts/check-pack-payload.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ const packInfo = JSON.parse(pack.stdout)[0];
assert(packInfo, "npm pack did not return package metadata");

const productionSources = walkFiles("src").filter(
(file) => file.endsWith(".ts") && !file.includes("/__tests__/") && !file.endsWith(".test.ts"),
(file) => file.endsWith(".ts") && !file.endsWith(".test.ts"),
);
const packedFiles = new Set(packInfo.files.map((file) => normalizePackagePath(file.path)));
const packageJson = JSON.parse(readFileSync(path.join(packageRoot, "package.json"), "utf8"));
Expand Down Expand Up @@ -129,7 +129,7 @@ assert(packageJson.openclaw?.build?.openclawVersion, "openclaw.build.openclawVer
assert(packageJson.openclaw?.build?.pluginSdkVersion, "openclaw.build.pluginSdkVersion is required");

for (const file of packedFiles) {
assert(!file.includes("__tests__"), `packed package includes test artifact ${file}`);
assert(!file.startsWith("test/"), `packed package includes test artifact ${file}`);
assert(!file.startsWith(".test-dist/"), `packed package includes test output ${file}`);
assert(!file.endsWith(".map"), `packed package includes source/declaration map ${file}`);
}
Expand Down
21 changes: 21 additions & 0 deletions integrations/openclaw/scripts/test-live.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

import { spawnSync } from "node:child_process";
import path from "node:path";
import { fileURLToPath } from "node:url";

const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");

const result = spawnSync(process.execPath, ["--test", ".test-dist/test/live-smoke.test.js"], {
cwd: packageRoot,
env: {
...process.env,
NEMO_FLOW_OPENCLAW_LIVE_SMOKE: "1",
},
stdio: "inherit",
});

process.exit(result.status ?? 1);
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ import {
NEMO_FLOW_OPENCLAW_JSON_SCHEMA,
nemoFlowConfigSchema,
parseConfig,
} from "../config.js";
} from "../src/config.js";
import {
defaultNemoFlowModuleLoader,
type NemoFlowModuleLoader,
type NemoFlowModules,
type NemoFlowRuntimeModule,
} from "../modules.js";
import { registerNemoFlowPlugin } from "../runtime-state.js";
} from "../src/modules.js";
import { registerNemoFlowPlugin } from "../src/runtime-state.js";
import type { OpenClawPluginApi, PluginLogger } from "openclaw/plugin-sdk/plugin-entry";
import { callGatewayStatus, type TestGatewayMethodHandler } from "./gateway-status.js";

Expand Down
Loading
Loading