Skip to content
Closed
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
107 changes: 7 additions & 100 deletions .github/workflows/code-scanning.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,110 +54,17 @@ jobs:
- name: Install ShellCheck
run: sudo apt-get update && sudo apt-get install -y shellcheck

- name: Collect shell files
id: shell-files
run: |
git ls-files '*.sh' 'install.sh' 'uninstall.sh' | sort -u > shell-files.txt
if [ -s shell-files.txt ]; then
echo "has_files=true" >> "$GITHUB_OUTPUT"
else
echo "has_files=false" >> "$GITHUB_OUTPUT"
fi
- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: "22"

- name: Generate ShellCheck SARIF
if: steps.shell-files.outputs.has_files == 'true'
run: |
# Ubuntu's packaged ShellCheck may not support --format=sarif.
# Generate json1 and convert it to SARIF for upload.
sc_exit=0
if xargs -r shellcheck --format=json1 < shell-files.txt > shellcheck.json; then
sc_exit=0
else
sc_exit=$?
fi

if jq -e . shellcheck.json >/dev/null 2>&1; then
jq '
def level_map:
if . == "error" then "error"
elif . == "warning" then "warning"
else "note"
end;
{
"version": "2.1.0",
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
"runs": [
{
"tool": {
"driver": {
"name": "ShellCheck",
"informationUri": "https://www.shellcheck.net/",
"rules": (
.comments
| map(select(.code != null) | {
"id": ("SC" + (.code | tostring)),
"name": ("SC" + (.code | tostring)),
"shortDescription": { "text": .level }
}) | unique_by(.id)
)
}
},
"results": (
.comments
| map({
"ruleId": ("SC" + (.code | tostring)),
"level": (.level | level_map),
"message": { "text": .message },
"locations": [
{
"physicalLocation": {
"artifactLocation": { "uri": .file },
"region": (
{
"startLine": .line,
"startColumn": .column,
"endLine": .endLine,
"endColumn": .endColumn
} | with_entries(select(.value != null))
)
}
}
]
})
)
}
]
}
' shellcheck.json > shellcheck.sarif
else
echo "ShellCheck produced invalid JSON (exit=$sc_exit); writing empty SARIF fallback."
cat > shellcheck.sarif <<'EOF'
{
"version": "2.1.0",
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
"runs": []
}
EOF
fi

if [ "$sc_exit" -ne 0 ]; then
echo "ShellCheck reported findings (exit=$sc_exit); continuing so SARIF can be uploaded."
fi

- name: Check SARIF has runs
id: sarif-runs
if: steps.shell-files.outputs.has_files == 'true'
run: |
run_count="$(jq '.runs | length' shellcheck.sarif)"
if [ "$run_count" -gt 0 ]; then
echo "has_runs=true" >> "$GITHUB_OUTPUT"
else
echo "has_runs=false" >> "$GITHUB_OUTPUT"
echo "Skipping SARIF upload because shellcheck.sarif has zero runs."
fi
id: shellcheck-sarif
run: node --no-warnings --experimental-strip-types scripts/github/shellcheck-sarif.ts

- name: Upload ShellCheck SARIF
if: steps.shell-files.outputs.has_files == 'true' && steps.sarif-runs.outputs.has_runs == 'true'
if: steps.shellcheck-sarif.outputs.has_files == 'true' && steps.shellcheck-sarif.outputs.has_runs == 'true'
uses: github/codeql-action/upload-sarif@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4
with:
sarif_file: shellcheck.sarif
36 changes: 9 additions & 27 deletions .github/workflows/docs-links-pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,35 +26,17 @@ jobs:
with:
fetch-depth: 0

- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: "22"

- name: Determine changed documentation files
id: changed
shell: bash
run: |
set -euo pipefail
base="${{ github.event.pull_request.base.sha }}"
head="${{ github.event.pull_request.head.sha }}"
mapfile -t doc_files < <(
git diff --name-only --diff-filter=ACMR "$base" "$head" -- \
'*.md' \
'*.mdx' \
':(exclude)node_modules/**' \
':(exclude)dist/**' \
':(exclude)vendor/**' \
':(exclude)build/**' \
| LC_ALL=C sort -u
)

if [[ "${#doc_files[@]}" -eq 0 ]]; then
echo "has_files=false" >> "$GITHUB_OUTPUT"
exit 0
fi

echo "has_files=true" >> "$GITHUB_OUTPUT"
{
echo "files<<EOF"
printf '%s\n' "${doc_files[@]}"
echo "EOF"
} >> "$GITHUB_OUTPUT"
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: node --no-warnings --experimental-strip-types scripts/github/changed-docs.ts

- name: Run markdown link checker
if: steps.changed.outputs.has_files == 'true'
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/docs-preview-pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ jobs:
working-directory: ./fern
run: fern check

# Keep secret-bearing preview publishing inline in this workflow.
# Do not run PR-owned helper code with FERN_TOKEN or write-capable GH_TOKEN.
- name: Generate preview URL
if: ${{ steps.fern-preview.outputs.enabled == 'true' }}
id: generate-docs
Expand Down Expand Up @@ -90,8 +92,8 @@ jobs:

${MARKER}"

COMMENT_ID=$(gh api "repos/${{ github.repository }}/issues/${PR_NUMBER}/comments" \
--jq ".[] | select(.body | contains(\"${MARKER}\")) | .id" | head -1)
COMMENT_ID=$(gh api --paginate --slurp "repos/${{ github.repository }}/issues/${PR_NUMBER}/comments" |
jq -r --arg marker "$MARKER" '[.[][]] | map(select(.body | contains($marker))) | .[0].id // empty')

if [ -n "$COMMENT_ID" ]; then
gh api "repos/${{ github.repository }}/issues/comments/${COMMENT_ID}" \
Expand Down
118 changes: 21 additions & 97 deletions .github/workflows/e2e-scenarios.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ jobs:
node-version: 22
cache: npm

- name: Set up Node on Windows host
if: startsWith(inputs.scenario, 'wsl-')
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: 22

- name: Install root dependencies
if: ${{ !startsWith(inputs.scenario, 'wsl-') }}
run: npm ci --ignore-scripts
Expand Down Expand Up @@ -147,125 +153,43 @@ jobs:

- name: Resolve workspace paths for WSL
if: startsWith(inputs.scenario, 'wsl-')
shell: powershell
run: |
$winPath = "${{ github.workspace }}"
$drive = $winPath.Substring(0,1).ToLower()
$rest = $winPath.Substring(2).Replace('\','/')
$wslCheckoutPath = "/mnt/$drive$rest"
$wslWorkdir = "/tmp/nemoclaw-scenario-wsl/${env:GITHUB_RUN_ID}-${env:GITHUB_RUN_ATTEMPT}"
"WSL_CHECKOUT_DIR=$wslCheckoutPath" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
"WSL_WORKDIR=$wslWorkdir" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
env:
WSL_WORKDIR_PREFIX: /tmp/nemoclaw-scenario-wsl
run: node --no-warnings --experimental-strip-types scripts/github/wsl.ts resolve-paths

- name: Ensure Ubuntu WSL exists
if: startsWith(inputs.scenario, 'wsl-')
shell: powershell
run: |
wsl --list --verbose 2>&1 | Out-Default
$null = wsl -d $env:WSL_DISTRO -- echo ok 2>&1
if ($LASTEXITCODE -ne 0) {
wsl --install -d $env:WSL_DISTRO --no-launch --web-download
wsl -d $env:WSL_DISTRO -- bash -c 'echo distro initialised'
}
wsl --set-default $env:WSL_DISTRO
run: node --no-warnings --experimental-strip-types scripts/github/wsl.ts ensure-ubuntu

- name: Install WSL dependencies
if: startsWith(inputs.scenario, 'wsl-')
shell: powershell
run: |
$script = @'
set -euo pipefail
export DEBIAN_FRONTEND=noninteractive
printf '%s\n' 'Acquire::ForceIPv4 "true";' 'Acquire::Retries "5";' >/etc/apt/apt.conf.d/99github-actions-network
apt-get update
apt-get install -y bash ca-certificates curl git jq lsb-release make python3 python3-pip rsync tar unzip xz-utils
if ! docker info >/dev/null 2>&1; then
apt-get install -y docker.io
service docker start || /etc/init.d/docker start || true
timeout 30 bash -c 'until docker info >/dev/null 2>&1; do sleep 2; done'
fi
curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
apt-get install -y nodejs
node --version
npm --version
docker --version
docker info >/dev/null
'@
$tmp = "$env:RUNNER_TEMP\wsl-step.sh"
[IO.File]::WriteAllText($tmp, ($script -replace "`r",""), (New-Object System.Text.UTF8Encoding $false))
$wslTmp = wsl -d $env:WSL_DISTRO -- wslpath -u ($tmp -replace '\\','/')
wsl -d $env:WSL_DISTRO -- bash -l $wslTmp
env:
WSL_INSTALL_DOCKER: "1"
run: node --no-warnings --experimental-strip-types scripts/github/wsl.ts install-ubuntu-deps

- name: Install Node.js 22 in WSL
if: startsWith(inputs.scenario, 'wsl-')
run: node --no-warnings --experimental-strip-types scripts/github/wsl.ts install-node

- name: Copy checkout into WSL ext4 workspace
if: startsWith(inputs.scenario, 'wsl-')
shell: powershell
run: |
$script = @"
set -euo pipefail
rm -rf '$env:WSL_WORKDIR'
mkdir -p /tmp/nemoclaw-scenario-wsl
rsync -a --no-owner --no-group --delete --exclude '/node_modules/' --exclude '/nemoclaw/node_modules/' --exclude '/nemoclaw-blueprint/.venv/' '$env:WSL_CHECKOUT_DIR'/ '$env:WSL_WORKDIR'/
git config --global --add safe.directory '$env:WSL_WORKDIR'
git -C '$env:WSL_WORKDIR' reset --hard HEAD
git -C '$env:WSL_WORKDIR' clean -ffdx
"@
$tmp = "$env:RUNNER_TEMP\wsl-step.sh"
[IO.File]::WriteAllText($tmp, ($script -replace "`r",""), (New-Object System.Text.UTF8Encoding $false))
$wslTmp = wsl -d $env:WSL_DISTRO -- wslpath -u ($tmp -replace '\\','/')
wsl -d $env:WSL_DISTRO -- bash -l $wslTmp
run: node --no-warnings --experimental-strip-types scripts/github/wsl.ts copy-checkout

- name: Install root dependencies in WSL
if: startsWith(inputs.scenario, 'wsl-')
shell: powershell
run: |
$script = @"
set -euo pipefail
cd '$env:WSL_WORKDIR'
npm ci --ignore-scripts
mkdir -p .e2e
bash test/e2e/runtime/coverage-report.sh > .e2e/coverage.md
"@
$tmp = "$env:RUNNER_TEMP\wsl-step.sh"
[IO.File]::WriteAllText($tmp, ($script -replace "`r",""), (New-Object System.Text.UTF8Encoding $false))
$wslTmp = wsl -d $env:WSL_DISTRO -- wslpath -u ($tmp -replace '\\','/')
wsl -d $env:WSL_DISTRO -- bash -l $wslTmp
run: node --no-warnings --experimental-strip-types scripts/github/wsl.ts install-root-and-render-coverage

- name: Run scenario in WSL
if: startsWith(inputs.scenario, 'wsl-')
shell: powershell
env:
NVIDIA_API_KEY: ${{ secrets.NVIDIA_API_KEY }}
E2E_SUITE_FILTER: ${{ inputs.suite_filter }}
SCENARIO: ${{ inputs.scenario }}
run: |
$env:WSLENV = "NVIDIA_API_KEY:E2E_SUITE_FILTER:NEMOCLAW_RECREATE_SANDBOX:SCENARIO:WSL_WORKDIR"
$script = @'
set -euo pipefail
cd "$WSL_WORKDIR"
export NVIDIA_API_KEY
export E2E_SUITE_FILTER
export NEMOCLAW_RECREATE_SANDBOX
bash test/e2e/runtime/run-scenario.sh "$SCENARIO"
'@
$tmp = "$env:RUNNER_TEMP\wsl-step.sh"
[IO.File]::WriteAllText($tmp, ($script -replace "`r",""), (New-Object System.Text.UTF8Encoding $false))
$wslTmp = wsl -d $env:WSL_DISTRO -- wslpath -u ($tmp -replace '\\','/')
wsl -d $env:WSL_DISTRO -- bash -l $wslTmp
run: node --no-warnings --experimental-strip-types scripts/github/wsl.ts run-scenario

- name: Copy WSL artifacts back to checkout
if: always() && startsWith(inputs.scenario, 'wsl-')
shell: powershell
run: |
$script = @"
set -euo pipefail
mkdir -p '$env:WSL_CHECKOUT_DIR/.e2e' '$env:WSL_CHECKOUT_DIR/test/e2e/logs'
if [ -d '$env:WSL_WORKDIR/.e2e' ]; then rsync -a '$env:WSL_WORKDIR/.e2e'/ '$env:WSL_CHECKOUT_DIR/.e2e'/; fi
if [ -d '$env:WSL_WORKDIR/test/e2e/logs' ]; then rsync -a '$env:WSL_WORKDIR/test/e2e/logs'/ '$env:WSL_CHECKOUT_DIR/test/e2e/logs'/; fi
"@
$tmp = "$env:RUNNER_TEMP\wsl-step.sh"
[IO.File]::WriteAllText($tmp, ($script -replace "`r",""), (New-Object System.Text.UTF8Encoding $false))
$wslTmp = wsl -d $env:WSL_DISTRO -- wslpath -u ($tmp -replace '\\','/')
wsl -d $env:WSL_DISTRO -- bash -l $wslTmp
run: node --no-warnings --experimental-strip-types scripts/github/wsl.ts copy-artifacts-to-checkout

- name: Upload scenario artifacts
if: always()
Expand Down
Loading
Loading