Skip to content

Commit 01070c0

Browse files
authored
feat: hello world, nemoclaw community (#2)
1 parent 380e998 commit 01070c0

File tree

17 files changed

+1250
-144
lines changed

17 files changed

+1250
-144
lines changed
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
name: Build Sandbox Images
5+
6+
on:
7+
push:
8+
branches: [main]
9+
paths:
10+
- "sandboxes/**"
11+
pull_request:
12+
branches: [main]
13+
paths:
14+
- "sandboxes/**"
15+
workflow_dispatch:
16+
17+
env:
18+
REGISTRY: ghcr.io
19+
IMAGE_PREFIX: ${{ github.repository }}
20+
21+
permissions:
22+
contents: read
23+
packages: write
24+
25+
jobs:
26+
# ---------------------------------------------------------------------------
27+
# Detect which sandbox images have changed
28+
# ---------------------------------------------------------------------------
29+
detect-changes:
30+
name: Detect changed sandboxes
31+
runs-on: ubuntu-latest
32+
outputs:
33+
base-changed: ${{ steps.changes.outputs.base_changed }}
34+
sandboxes: ${{ steps.changes.outputs.sandboxes }}
35+
steps:
36+
- uses: actions/checkout@v4
37+
with:
38+
fetch-depth: 0
39+
40+
- name: Determine changed sandboxes
41+
id: changes
42+
run: |
43+
set -euo pipefail
44+
45+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
46+
# Build everything on manual trigger
47+
BASE_CHANGED="true"
48+
SANDBOXES=$(find sandboxes -mindepth 1 -maxdepth 1 -type d -name '*.base' -prune -o -exec test -f {}/Dockerfile \; -print \
49+
| xargs -I{} basename {} \
50+
| grep -v '^base$' \
51+
| jq -R -s -c 'split("\n") | map(select(length > 0))')
52+
else
53+
if [ "${{ github.event_name }}" = "pull_request" ]; then
54+
BASE_SHA="${{ github.event.pull_request.base.sha }}"
55+
else
56+
BASE_SHA="${{ github.event.before }}"
57+
fi
58+
59+
CHANGED=$(git diff --name-only "$BASE_SHA" "${{ github.sha }}" -- sandboxes/)
60+
61+
# Check if base changed
62+
if echo "$CHANGED" | grep -q '^sandboxes/base/'; then
63+
BASE_CHANGED="true"
64+
# When base changes, rebuild all sandboxes that have a Dockerfile
65+
SANDBOXES=$(find sandboxes -mindepth 1 -maxdepth 1 -type d -exec test -f {}/Dockerfile \; -print \
66+
| xargs -I{} basename {} \
67+
| grep -v '^base$' \
68+
| jq -R -s -c 'split("\n") | map(select(length > 0))')
69+
else
70+
BASE_CHANGED="false"
71+
SANDBOXES=$(echo "$CHANGED" \
72+
| cut -d'/' -f2 \
73+
| sort -u \
74+
| while read -r name; do
75+
if [ "$name" != "base" ] && [ -f "sandboxes/${name}/Dockerfile" ]; then echo "$name"; fi
76+
done \
77+
| jq -R -s -c 'split("\n") | map(select(length > 0))')
78+
fi
79+
fi
80+
81+
echo "base_changed=${BASE_CHANGED}" >> "$GITHUB_OUTPUT"
82+
echo "sandboxes=${SANDBOXES}" >> "$GITHUB_OUTPUT"
83+
echo "Base changed: ${BASE_CHANGED}"
84+
echo "Will build: ${SANDBOXES}"
85+
86+
# ---------------------------------------------------------------------------
87+
# Build the base sandbox image (other sandboxes depend on this)
88+
# ---------------------------------------------------------------------------
89+
build-base:
90+
name: Build base
91+
needs: detect-changes
92+
if: needs.detect-changes.outputs.base-changed == 'true'
93+
runs-on: ubuntu-latest
94+
steps:
95+
- uses: actions/checkout@v4
96+
97+
- name: Lowercase image prefix
98+
id: repo
99+
run: echo "image_prefix=${IMAGE_PREFIX,,}" >> "$GITHUB_OUTPUT"
100+
101+
- name: Set up QEMU
102+
uses: docker/setup-qemu-action@v3
103+
104+
- name: Set up Docker Buildx
105+
uses: docker/setup-buildx-action@v3
106+
107+
- name: Log in to GHCR
108+
uses: docker/login-action@v3
109+
with:
110+
registry: ${{ env.REGISTRY }}
111+
username: ${{ github.actor }}
112+
password: ${{ secrets.GITHUB_TOKEN }}
113+
114+
- name: Generate image metadata
115+
id: meta
116+
uses: docker/metadata-action@v5
117+
with:
118+
images: ${{ env.REGISTRY }}/${{ steps.repo.outputs.image_prefix }}/sandboxes/base
119+
tags: |
120+
type=sha,prefix=
121+
type=raw,value=latest,enable={{is_default_branch}}
122+
123+
- name: Build and push
124+
uses: docker/build-push-action@v6
125+
with:
126+
context: sandboxes/base
127+
platforms: linux/amd64,linux/arm64
128+
push: ${{ github.ref == 'refs/heads/main' }}
129+
tags: ${{ steps.meta.outputs.tags }}
130+
labels: ${{ steps.meta.outputs.labels }}
131+
cache-from: type=gha,scope=base
132+
cache-to: type=gha,mode=max,scope=base
133+
134+
# ---------------------------------------------------------------------------
135+
# Build dependent sandbox images (after base completes)
136+
# ---------------------------------------------------------------------------
137+
build:
138+
name: Build ${{ matrix.sandbox }}
139+
needs: [detect-changes, build-base]
140+
if: |
141+
always() &&
142+
needs.detect-changes.result == 'success' &&
143+
(needs.build-base.result == 'success' || needs.build-base.result == 'skipped') &&
144+
needs.detect-changes.outputs.sandboxes != '[]'
145+
runs-on: ubuntu-latest
146+
strategy:
147+
fail-fast: false
148+
matrix:
149+
sandbox: ${{ fromJson(needs.detect-changes.outputs.sandboxes) }}
150+
steps:
151+
- uses: actions/checkout@v4
152+
153+
- name: Lowercase image prefix
154+
id: repo
155+
run: echo "image_prefix=${IMAGE_PREFIX,,}" >> "$GITHUB_OUTPUT"
156+
157+
- name: Set up QEMU
158+
uses: docker/setup-qemu-action@v3
159+
160+
# On PRs, use a local registry so the base image built in this job is
161+
# accessible to the subsequent buildx build (docker-container driver
162+
# cannot see images loaded into the host daemon).
163+
- name: Start local registry (PR only)
164+
if: github.ref != 'refs/heads/main'
165+
run: docker run -d -p 5000:5000 --name registry registry:2
166+
167+
- name: Set up Docker Buildx
168+
uses: docker/setup-buildx-action@v3
169+
with:
170+
driver-opts: ${{ github.ref != 'refs/heads/main' && 'network=host' || '' }}
171+
172+
- name: Log in to GHCR
173+
uses: docker/login-action@v3
174+
with:
175+
registry: ${{ env.REGISTRY }}
176+
username: ${{ github.actor }}
177+
password: ${{ secrets.GITHUB_TOKEN }}
178+
179+
# On PRs the base image is not in GHCR. Build it locally, push to the
180+
# local registry, and override BASE_IMAGE to point there.
181+
- name: Build base image locally (PR only)
182+
if: github.ref != 'refs/heads/main'
183+
uses: docker/build-push-action@v6
184+
with:
185+
context: sandboxes/base
186+
push: true
187+
tags: localhost:5000/sandboxes/base:latest
188+
cache-from: type=gha,scope=base
189+
190+
- name: Set BASE_IMAGE
191+
id: base
192+
run: |
193+
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
194+
echo "image=${{ env.REGISTRY }}/${{ steps.repo.outputs.image_prefix }}/sandboxes/base:latest" >> "$GITHUB_OUTPUT"
195+
else
196+
echo "image=localhost:5000/sandboxes/base:latest" >> "$GITHUB_OUTPUT"
197+
fi
198+
199+
- name: Generate image metadata
200+
id: meta
201+
uses: docker/metadata-action@v5
202+
with:
203+
images: ${{ env.REGISTRY }}/${{ steps.repo.outputs.image_prefix }}/sandboxes/${{ matrix.sandbox }}
204+
tags: |
205+
type=sha,prefix=
206+
type=raw,value=latest,enable={{is_default_branch}}
207+
208+
- name: Build and push
209+
uses: docker/build-push-action@v6
210+
with:
211+
context: sandboxes/${{ matrix.sandbox }}
212+
platforms: ${{ github.ref == 'refs/heads/main' && 'linux/amd64,linux/arm64' || 'linux/amd64' }}
213+
push: ${{ github.ref == 'refs/heads/main' }}
214+
tags: ${{ steps.meta.outputs.tags }}
215+
labels: ${{ steps.meta.outputs.labels }}
216+
build-args: |
217+
BASE_IMAGE=${{ steps.base.outputs.image }}
218+
cache-from: type=gha,scope=${{ matrix.sandbox }}
219+
cache-to: type=gha,mode=max,scope=${{ matrix.sandbox }}

.github/workflows/dco.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: DCO
2+
3+
on:
4+
pull_request:
5+
branches: [main]
6+
7+
jobs:
8+
dco:
9+
name: DCO Check
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v4
13+
with:
14+
fetch-depth: 0
15+
16+
- name: Check DCO sign-off
17+
uses: gsactions/dco-check@v1.1.1
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
name: License Headers
5+
6+
on:
7+
push:
8+
branches: [main]
9+
pull_request:
10+
branches: [main]
11+
12+
permissions:
13+
contents: read
14+
15+
jobs:
16+
license-headers:
17+
name: Check License Headers
18+
runs-on: ubuntu-latest
19+
steps:
20+
- uses: actions/checkout@v4
21+
22+
- uses: actions/setup-python@v5
23+
with:
24+
python-version: "3.12"
25+
26+
- name: Check SPDX license headers
27+
run: python scripts/check_license_headers.py --check

CONTRIBUTING.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Contributing to NemoClaw Community
2+
3+
Thank you for your interest in contributing to the NemoClaw Community ecosystem. This guide covers everything you need to get started.
4+
5+
## Ways to Contribute
6+
7+
- **Sandbox images** -- Add new domain-specific sandbox environments under `sandboxes/`
8+
- **Skills** -- Create agent skills and tool definitions inside a sandbox's `skills/` directory
9+
- **Bug fixes** -- Fix issues in existing sandboxes, skills, or configurations
10+
- **Documentation** -- Improve READMEs, guides, and usage examples
11+
- **Integrations** -- Connect NemoClaw to new tools, platforms, or workflows
12+
13+
## Getting Started
14+
15+
1. Fork this repository
16+
2. Clone your fork locally
17+
3. Create a feature branch from `main`
18+
19+
```bash
20+
git clone https://github.com/<your-username>/NemoClaw-Community.git
21+
cd NemoClaw-Community
22+
git checkout -b my-feature
23+
```
24+
25+
## Adding a Sandbox Image
26+
27+
Each sandbox lives in its own directory under `sandboxes/`:
28+
29+
```
30+
sandboxes/my-sandbox/
31+
Dockerfile
32+
README.md
33+
...
34+
```
35+
36+
Requirements:
37+
- A `Dockerfile` that builds cleanly
38+
- A `README.md` describing the sandbox's purpose, usage, and any prerequisites
39+
- Keep images minimal -- only include what's needed for the workload
40+
41+
## Adding a Skill
42+
43+
Skills live inside their sandbox's `skills/` directory (e.g., `sandboxes/openclaw/skills/my-skill/`). Each skill should include:
44+
- A `SKILL.md` describing what it does and when to use it
45+
- Any supporting files the skill needs
46+
- A README with usage examples
47+
48+
## Submitting a Pull Request
49+
50+
1. Ensure your changes are focused -- one feature or fix per PR
51+
2. Include a clear description of what your PR does and why
52+
3. Test your changes locally before submitting
53+
4. Update any relevant documentation
54+
55+
## Development Guidelines
56+
57+
- Follow existing naming conventions and directory structures
58+
- Write clear commit messages
59+
- Keep PRs small and reviewable
60+
- Respond to review feedback promptly
61+
62+
## Reporting Issues
63+
64+
Use GitHub Issues for bug reports and feature requests. Include:
65+
- A clear title and description
66+
- Steps to reproduce (for bugs)
67+
- Expected vs. actual behavior
68+
- Environment details (OS, Docker version, GPU, etc.)
69+
70+
## License
71+
72+
By contributing, you agree that your contributions will be licensed under the Apache 2.0 License.

0 commit comments

Comments
 (0)