-
Notifications
You must be signed in to change notification settings - Fork 2k
sync: rebase on upstream (2026-03-26) #959
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f02d4fa
705a5c6
6b2acf7
be8bffd
2099a69
2c4d391
ed63c2b
6e5712c
4fdeb92
54422a2
6a953ad
4f864c0
5f4737a
9fc5d8c
413f181
58512c5
a86248c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| name: CI | ||
|
|
||
| on: | ||
| push: | ||
| branches: [main] | ||
| pull_request: | ||
| branches: [main] | ||
|
|
||
| jobs: | ||
| build-and-push: | ||
| runs-on: [self-hosted, Linux, X64] | ||
| permissions: | ||
| contents: read | ||
| packages: write | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Log in to GitHub Container Registry | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| registry: ghcr.io | ||
| username: ${{ github.actor }} | ||
| password: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Set up Docker Buildx | ||
| uses: docker/setup-buildx-action@v3 | ||
|
|
||
| - name: Build and push | ||
| uses: docker/build-push-action@v6 | ||
| with: | ||
| context: . | ||
| push: ${{ github.ref == 'refs/heads/main' }} | ||
| tags: | | ||
| ghcr.io/${{ github.repository }}:latest | ||
| ghcr.io/${{ github.repository }}:${{ github.sha }} | ||
| cache-from: type=gha | ||
| cache-to: type=gha,mode=max | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,101 @@ | ||||||||||||||||||||||||||||||||||
| name: Upstream Sync | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| on: | ||||||||||||||||||||||||||||||||||
| schedule: | ||||||||||||||||||||||||||||||||||
| # Daily at 06:30 UTC (offset from paperclip's 06:00) | ||||||||||||||||||||||||||||||||||
| - cron: "30 6 * * *" | ||||||||||||||||||||||||||||||||||
| workflow_dispatch: | ||||||||||||||||||||||||||||||||||
| inputs: | ||||||||||||||||||||||||||||||||||
| mode: | ||||||||||||||||||||||||||||||||||
| description: "Sync mode" | ||||||||||||||||||||||||||||||||||
| required: true | ||||||||||||||||||||||||||||||||||
| default: "pr" | ||||||||||||||||||||||||||||||||||
| type: choice | ||||||||||||||||||||||||||||||||||
| options: | ||||||||||||||||||||||||||||||||||
| - pr | ||||||||||||||||||||||||||||||||||
| - push | ||||||||||||||||||||||||||||||||||
| - dry-run | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| jobs: | ||||||||||||||||||||||||||||||||||
| sync: | ||||||||||||||||||||||||||||||||||
| runs-on: self-hosted | ||||||||||||||||||||||||||||||||||
| timeout-minutes: 20 | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
Comment on lines
+1
to
+23
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add workflow concurrency before this can push This workflow can be started by schedule, manual dispatch, and reruns, but there is no ⛔ Serialize sync runs name: Upstream Sync
+
+concurrency:
+ group: upstream-sync-main
+ cancel-in-progress: true
on:🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
| steps: | ||||||||||||||||||||||||||||||||||
| - name: Checkout (full history for rebase) | ||||||||||||||||||||||||||||||||||
| uses: actions/checkout@v4 | ||||||||||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||||||||||
| fetch-depth: 0 | ||||||||||||||||||||||||||||||||||
| token: ${{ secrets.GH_PAT }} | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| - name: Setup Node.js | ||||||||||||||||||||||||||||||||||
| uses: actions/setup-node@v4 | ||||||||||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||||||||||
| node-version: "20" | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| - name: Configure git | ||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||
| git config user.name "github-actions[bot]" | ||||||||||||||||||||||||||||||||||
| git config user.email "github-actions[bot]@users.noreply.github.com" | ||||||||||||||||||||||||||||||||||
| # Nuke stale credential helpers on self-hosted runner | ||||||||||||||||||||||||||||||||||
| git config --local --unset-all credential.helper 2>/dev/null || true | ||||||||||||||||||||||||||||||||||
| git config --global --unset-all credential.helper 2>/dev/null || true | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| - name: Clean working tree (self-hosted runner may have leftovers) | ||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||
| git checkout main 2>/dev/null || true | ||||||||||||||||||||||||||||||||||
| git rebase --abort 2>/dev/null || true | ||||||||||||||||||||||||||||||||||
| git reset --hard HEAD | ||||||||||||||||||||||||||||||||||
| git clean -fd | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| - name: Add upstream remote | ||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||
| git remote get-url upstream 2>/dev/null || \ | ||||||||||||||||||||||||||||||||||
| git remote add upstream https://github.com/NVIDIA/NemoClaw.git | ||||||||||||||||||||||||||||||||||
| git fetch upstream | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| - name: Install Agent SDK | ||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||
| rm -rf "$(npm root -g)/@anthropic-ai/.claude-code-"* "$(npm root -g)/@anthropic-ai/.claude-agent-sdk-"* 2>/dev/null || true | ||||||||||||||||||||||||||||||||||
| npm install -g @anthropic-ai/claude-code || npm install -g @anthropic-ai/claude-code | ||||||||||||||||||||||||||||||||||
| npm install -g @anthropic-ai/claude-agent-sdk || npm install -g @anthropic-ai/claude-agent-sdk | ||||||||||||||||||||||||||||||||||
| node -e "require('@anthropic-ai/claude-agent-sdk')" 2>/dev/null || echo "SDK import check: will resolve at runtime via global root" | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| - name: Write Claude credentials | ||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||
| mkdir -p ~/.claude | ||||||||||||||||||||||||||||||||||
| echo '${{ secrets.CLAUDE_CREDENTIALS_JSON }}' > ~/.claude/.credentials.json | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
Comment on lines
+64
to
+68
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Verify whether repository scripts reference the credentials file path.
rg -n --hidden -S '\.claude/\.credentials\.json|CLAUDE_CREDENTIALS_JSON|CLAUDE_CODE_OAUTH_TOKEN|ANTHROPIC_API_KEY'Repository: NVIDIA/NemoClaw Length of output: 1535 🏁 Script executed: cat scripts/upstream-sync.mjs | head -100Repository: NVIDIA/NemoClaw Length of output: 3273 🏁 Script executed: grep -n "credentials\|\.claude" scripts/upstream-sync.mjsRepository: NVIDIA/NemoClaw Length of output: 41 🏁 Script executed: wc -l scripts/upstream-sync.mjsRepository: NVIDIA/NemoClaw Length of output: 88 🏁 Script executed: cat -n scripts/upstream-sync.mjs | tail -300Repository: NVIDIA/NemoClaw Length of output: 11904 Remove the unused Claude credentials file write step. The - - name: Write Claude credentials
- run: |
- mkdir -p ~/.claude
- echo '${{ secrets.CLAUDE_CREDENTIALS_JSON }}' > ~/.claude/.credentials.json📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
| - name: Check for upstream changes | ||||||||||||||||||||||||||||||||||
| id: check | ||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||
| BEHIND=$(git rev-list HEAD..upstream/main --count) | ||||||||||||||||||||||||||||||||||
| echo "behind=$BEHIND" >> "$GITHUB_OUTPUT" | ||||||||||||||||||||||||||||||||||
| if [ "$BEHIND" -eq 0 ]; then | ||||||||||||||||||||||||||||||||||
| echo "Up to date with upstream. Skipping sync." | ||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||
| echo "Behind upstream by $BEHIND commits." | ||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| - name: Run upstream sync | ||||||||||||||||||||||||||||||||||
| if: steps.check.outputs.behind != '0' | ||||||||||||||||||||||||||||||||||
| env: | ||||||||||||||||||||||||||||||||||
| CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} | ||||||||||||||||||||||||||||||||||
| GH_TOKEN: ${{ secrets.GH_PAT }} | ||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||
| MODE="${{ github.event.inputs.mode || 'pr' }}" | ||||||||||||||||||||||||||||||||||
| case "$MODE" in | ||||||||||||||||||||||||||||||||||
| push) FLAG="--push" ;; | ||||||||||||||||||||||||||||||||||
| pr) FLAG="--pr" ;; | ||||||||||||||||||||||||||||||||||
| dry-run) FLAG="--dry-run" ;; | ||||||||||||||||||||||||||||||||||
| esac | ||||||||||||||||||||||||||||||||||
| node scripts/upstream-sync.mjs $FLAG | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
Comment on lines
+85
to
+93
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Quick check for unquoted variable expansions in workflow run blocks.
rg -n --hidden -S '\$[A-Za-z_][A-Za-z0-9_]*' .github/workflows/upstream-sync.ymlRepository: NVIDIA/NemoClaw Length of output: 292 Quote Line 92 should quote the variable to satisfy SC2086 and prevent word-splitting/globbing issues. Suggested fix- node scripts/upstream-sync.mjs $FLAG
+ node scripts/upstream-sync.mjs "$FLAG"📝 Committable suggestion
Suggested change
🧰 Tools🪛 actionlint (1.7.11)[error] 85-85: shellcheck reported issue in this script: SC2086:info:7:32: Double quote to prevent globbing and word splitting (shellcheck) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
| - name: Upload agent event log | ||||||||||||||||||||||||||||||||||
| if: always() | ||||||||||||||||||||||||||||||||||
| uses: actions/upload-artifact@v4 | ||||||||||||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||||||||||||
| name: agent-events-log | ||||||||||||||||||||||||||||||||||
| path: agent-events.log | ||||||||||||||||||||||||||||||||||
| if-no-files-found: ignore | ||||||||||||||||||||||||||||||||||
| retention-days: 14 | ||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -69,9 +69,18 @@ ENV NEMOCLAW_MODEL=${NEMOCLAW_MODEL} \ | |||||||||||||
| NEMOCLAW_INFERENCE_API=${NEMOCLAW_INFERENCE_API} \ | ||||||||||||||
| NEMOCLAW_INFERENCE_COMPAT_B64=${NEMOCLAW_INFERENCE_COMPAT_B64} | ||||||||||||||
|
|
||||||||||||||
| # WOPR sidecar — provision + health endpoints for nemoclaw-platform | ||||||||||||||
| COPY wopr/ /opt/wopr/ | ||||||||||||||
|
|
||||||||||||||
| WORKDIR /sandbox | ||||||||||||||
| USER sandbox | ||||||||||||||
|
|
||||||||||||||
| # Pre-create OpenClaw directories and write default config. | ||||||||||||||
| # These are saved to /opt/nemoclaw-defaults/ (read-only at runtime). | ||||||||||||||
| # The startup script copies them to $HOME/.openclaw/ (writable volume). | ||||||||||||||
| RUN mkdir -p /sandbox/.openclaw/agents/main/agent \ | ||||||||||||||
| && chmod 700 /sandbox/.openclaw | ||||||||||||||
|
|
||||||||||||||
| # Write the COMPLETE openclaw.json including gateway config and auth token. | ||||||||||||||
| # This file is immutable at runtime (Landlock read-only on /sandbox/.openclaw). | ||||||||||||||
| # No runtime writes to openclaw.json are needed or possible. | ||||||||||||||
|
|
@@ -118,7 +127,6 @@ path = os.path.expanduser('~/.openclaw/openclaw.json'); \ | |||||||||||||
| json.dump(config, open(path, 'w'), indent=2); \ | ||||||||||||||
| os.chmod(path, 0o600)" | ||||||||||||||
|
|
||||||||||||||
| # Install NemoClaw plugin into OpenClaw | ||||||||||||||
| RUN openclaw doctor --fix > /dev/null 2>&1 || true \ | ||||||||||||||
| && openclaw plugins install /opt/nemoclaw > /dev/null 2>&1 || true | ||||||||||||||
|
|
||||||||||||||
|
|
@@ -146,6 +154,18 @@ RUN sha256sum /sandbox/.openclaw/openclaw.json > /sandbox/.openclaw/.config-hash | |||||||||||||
| && chmod 444 /sandbox/.openclaw/.config-hash \ | ||||||||||||||
| && chown root:root /sandbox/.openclaw/.config-hash | ||||||||||||||
|
|
||||||||||||||
| # Save build-time config as defaults — startup script copies to writable HOME | ||||||||||||||
| RUN cp -a /sandbox/.openclaw /opt/nemoclaw-defaults \ | ||||||||||||||
| && cp -a /sandbox/.nemoclaw /opt/nemoclaw-defaults/.nemoclaw | ||||||||||||||
|
Comment on lines
+157
to
+159
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The default snapshot still points back into
🐛 One minimal fix-RUN cp -a /sandbox/.openclaw /opt/nemoclaw-defaults \
+RUN cp -aL /sandbox/.openclaw /opt/nemoclaw-defaults \
&& cp -a /sandbox/.nemoclaw /opt/nemoclaw-defaults/.nemoclaw📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||
| USER sandbox | ||||||||||||||
|
|
||||||||||||||
| # At runtime, HOME=/data (writable volume mount from FleetManager). | ||||||||||||||
| # ReadonlyRootfs makes /sandbox read-only, so all writes go to /data. | ||||||||||||||
| ENV HOME=/data | ||||||||||||||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
|
|
||||||||||||||
| # Expose WOPR sidecar port | ||||||||||||||
| EXPOSE 3100 | ||||||||||||||
|
|
||||||||||||||
| # Entrypoint runs as root to start the gateway as the gateway user, | ||||||||||||||
| # then drops to sandbox for agent commands. See nemoclaw-start.sh. | ||||||||||||||
| ENTRYPOINT ["/usr/local/bin/nemoclaw-start"] | ||||||||||||||
|
|
||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -207,6 +207,20 @@ PYAUTOPAIR | |||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| # ── Main ───────────────────────────────────────────────────────── | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| # Copy default config to writable HOME if not already present. | ||||||||||||||||||||||||||||||||||
| # FleetManager mounts a volume at /data and sets HOME=/data. | ||||||||||||||||||||||||||||||||||
| # The rootfs is read-only, so all writes must go to /data. | ||||||||||||||||||||||||||||||||||
| if [ -d /opt/nemoclaw-defaults ] && [ ! -f "${HOME}/.openclaw/openclaw.json" ]; then | ||||||||||||||||||||||||||||||||||
| echo "[init] Copying default config to ${HOME}/.openclaw/" | ||||||||||||||||||||||||||||||||||
| mkdir -p "${HOME}/.openclaw" "${HOME}/.nemoclaw" | ||||||||||||||||||||||||||||||||||
| cp -a /opt/nemoclaw-defaults/. "${HOME}/.openclaw/" | ||||||||||||||||||||||||||||||||||
| [ -d /opt/nemoclaw-defaults/.nemoclaw ] && cp -a /opt/nemoclaw-defaults/.nemoclaw/. "${HOME}/.nemoclaw/" | ||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| # Ensure writable dirs exist | ||||||||||||||||||||||||||||||||||
| mkdir -p "${HOME}/.openclaw/agents/main/agent" 2>/dev/null || true | ||||||||||||||||||||||||||||||||||
| touch /tmp/gateway.log 2>/dev/null || true | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| echo 'Setting up NemoClaw...' | ||||||||||||||||||||||||||||||||||
| [ -f .env ] && chmod 600 .env | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
|
@@ -293,6 +307,13 @@ echo "[gateway] openclaw gateway launched as 'gateway' user (pid $GATEWAY_PID)" | |||||||||||||||||||||||||||||||||
| start_auto_pair | ||||||||||||||||||||||||||||||||||
| print_dashboard_urls | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| # Start WOPR sidecar in foreground — keeps the container alive. | ||||||||||||||||||||||||||||||||||
| # Serves /internal/health + /internal/provision for nemoclaw-platform. | ||||||||||||||||||||||||||||||||||
| if [ -f /opt/wopr/sidecar.js ]; then | ||||||||||||||||||||||||||||||||||
| echo "[wopr-sidecar] starting in foreground (port ${PORT:-3100})" | ||||||||||||||||||||||||||||||||||
| exec node /opt/wopr/sidecar.js | ||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+310
to
+315
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Keep PID 1 supervising both the gateway and the sidecar. After the background launch at Lines 303-305, 🔁 One way to keep both processes under supervision if [ -f /opt/wopr/sidecar.js ]; then
echo "[wopr-sidecar] starting in foreground (port ${PORT:-3100})"
- exec node /opt/wopr/sidecar.js
+ node /opt/wopr/sidecar.js &
+ SIDECAR_PID=$!
+ trap 'kill "$GATEWAY_PID" "$SIDECAR_PID" 2>/dev/null || true' EXIT INT TERM
+ wait -n "$GATEWAY_PID" "$SIDECAR_PID"
+ exit $?
fi📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| # Keep container running by waiting on the gateway process. | ||||||||||||||||||||||||||||||||||
| # This script is PID 1 (ENTRYPOINT); if it exits, Docker kills all children. | ||||||||||||||||||||||||||||||||||
| wait "$GATEWAY_PID" | ||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't execute fork PRs on the self-hosted runner.
pull_requestplusruns-on: [self-hosted, ...]lets untrusted PR code run arbitrary Docker build steps on your private runner. Keep self-hosted execution for trusted pushes/same-repo branches only, or move PR builds to GitHub-hosted runners.🔒 Minimal guard
jobs: build-and-push: + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository runs-on: [self-hosted, Linux, X64]📝 Committable suggestion
🤖 Prompt for AI Agents