Skip to content

Commit a86b6c7

Browse files
committed
Merge branch 'main' of github.com:NVIDIA-NeMo/RL into jveronvialard/bt-rm-training
2 parents 179767e + 01b8532 commit a86b6c7

File tree

90 files changed

+2038
-374
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

90 files changed

+2038
-374
lines changed
Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
name: "Submodule Fast-Forward Check"
16+
17+
on:
18+
workflow_call:
19+
inputs:
20+
base_ref:
21+
required: true
22+
type: string
23+
description: "Target branch to check against"
24+
head_ref:
25+
required: true
26+
type: string
27+
description: "Feature branch name"
28+
pr_number:
29+
required: true
30+
type: string
31+
description: "Pull request number"
32+
head_sha:
33+
required: true
34+
type: string
35+
description: "Head commit SHA of the feature branch"
36+
37+
jobs:
38+
check:
39+
name: Check submodule fast-forward
40+
runs-on: ubuntu-latest
41+
outputs:
42+
failed: ${{ steps.check.outputs.failed }}
43+
changed: ${{ steps.check.outputs.changed }}
44+
comment_body: ${{ steps.check.outputs.comment_body }}
45+
steps:
46+
- name: Checkout repository
47+
uses: actions/checkout@v4
48+
with:
49+
submodules: 'recursive'
50+
51+
- name: Fetch target branch reference
52+
run: |
53+
git fetch origin ${{ inputs.base_ref }}
54+
55+
- name: Check submodule fast-forward status
56+
id: check
57+
shell: bash -x -e {0}
58+
run: |
59+
echo "Checking submodules are fast-forwarded..."
60+
61+
# Get current submodule status
62+
echo "Current submodule status:"
63+
git submodule status
64+
65+
failed=0
66+
changed=0
67+
success_body=""
68+
failed_body=""
69+
70+
# Process each submodule from git submodule status
71+
while read -r line; do
72+
# Extract commit and path from: " <commit> <path> (<branch_info>)"
73+
current_commit=$(echo "$line" | awk '{print $1}' | sed 's/^[+-]//')
74+
submodule_path=$(echo "$line" | awk '{print $2}')
75+
76+
if [[ -z "$current_commit" ]] || [[ -z "$submodule_path" ]]; then
77+
continue
78+
fi
79+
80+
submodule_name=$(basename "$submodule_path")
81+
echo ""
82+
echo "Checking $submodule_name at $submodule_path"
83+
echo "Current commit: $current_commit"
84+
85+
# Get target branch commit for this submodule
86+
target_commit=$(git ls-tree origin/${{ inputs.base_ref }} "$submodule_path" | awk '{print $3}')
87+
88+
if [[ -z "$target_commit" ]]; then
89+
echo "❌ Could not find $submodule_name in ${{ inputs.base_ref }} branch"
90+
failed=1
91+
continue
92+
fi
93+
94+
echo "Target commit: $target_commit"
95+
96+
# Analyze the relationship between target and current commits
97+
cd "$submodule_path"
98+
99+
# Check if this is a shallow repository and unshallow if needed
100+
if git rev-parse --is-shallow-repository >/dev/null 2>&1 && [ "$(git rev-parse --is-shallow-repository)" = "true" ]; then
101+
echo "📦 $submodule_name: Detected shallow clone, fetching full history..."
102+
git fetch --unshallow >/dev/null 2>&1 || {
103+
echo "⚠️ Warning: Failed to unshallow repository. Ancestry checks may be limited."
104+
}
105+
fi
106+
107+
# Get GitHub repository URL for comment
108+
remote_url=$(git remote get-url origin 2>/dev/null || echo "")
109+
if [[ "$remote_url" == *.git ]]; then
110+
github_repo="${remote_url%.git}"
111+
else
112+
github_repo="$remote_url"
113+
fi
114+
115+
# Case 1: Same commit
116+
if [[ "$current_commit" = "$target_commit" ]]; then
117+
echo "✅ $submodule_name: PR branch matches ${{ inputs.base_ref }} branch (same commit)"
118+
# No change, so don't add to changed count or comment
119+
120+
# Case 2: Check if target commit is an ancestor of current commit (current is fast-forward)
121+
elif git merge-base --is-ancestor "$target_commit" "$current_commit" 2>/dev/null; then
122+
echo "✅ $submodule_name: PR branch is ahead of ${{ inputs.base_ref }} branch (fast-forward)"
123+
echo "📊 Commits added in PR #${{ inputs.pr_number }} (${{ inputs.head_ref }} branch):"
124+
git log --oneline --graph "$target_commit".."$current_commit" 2>/dev/null || echo " (Unable to show progression - possibly shallow clone)"
125+
changed=1
126+
success_body+="$submodule_name: ✅ PR branch is ahead of ${{ inputs.base_ref }} branch (fast-forward)"$'\n'
127+
128+
# Case 3: Check if current commit is an ancestor of target commit (current is behind)
129+
elif git merge-base --is-ancestor "$current_commit" "$target_commit" 2>/dev/null; then
130+
echo "❌ $submodule_name: PR branch is BEHIND ${{ inputs.base_ref }} branch"
131+
echo " Submodule needs to be updated to include recent changes from ${{ inputs.base_ref }}"
132+
echo "📊 Missing commits from ${{ inputs.base_ref }} that should be included:"
133+
git log --oneline --graph "$current_commit".."$target_commit" 2>/dev/null || echo " (Unable to show missing commits)"
134+
failed=1
135+
changed=1
136+
if [[ -n "$github_repo" && "$github_repo" == https://github.com/* ]]; then
137+
failed_body+="$submodule_name: ❌ PR branch is BEHIND ${{ inputs.base_ref }} branch"$'\n'
138+
failed_body+=" TARGET (${{ inputs.base_ref }} branch): $github_repo/commits/$target_commit/"$'\n'
139+
failed_body+=" CURRENT (PR #${{ inputs.pr_number }} from ${{ inputs.head_ref }}): $github_repo/commits/$current_commit/"$'\n\n'
140+
fi
141+
142+
else
143+
# Case 4: Commits have diverged or have no common ancestor
144+
common_ancestor=$(git merge-base "$target_commit" "$current_commit" 2>/dev/null)
145+
146+
if [ -n "$common_ancestor" ]; then
147+
echo "❌ $submodule_name: Commits have DIVERGED from a common ancestor"
148+
echo " This indicates parallel development - manual merge may be required"
149+
echo ""
150+
echo "📊 Divergence analysis:"
151+
echo " Common ancestor: $common_ancestor"
152+
git log --oneline -1 "$common_ancestor" 2>/dev/null || echo " (Unable to show common ancestor)"
153+
echo ""
154+
echo " For detailed commit history inspection:"
155+
failed=1
156+
changed=1
157+
if [[ -n "$github_repo" && "$github_repo" == https://github.com/* ]]; then
158+
echo " TARGET (${{ inputs.base_ref }} branch): $github_repo/commits/$target_commit/"
159+
echo " CURRENT (PR #${{ inputs.pr_number }} from ${{ inputs.head_ref }}): $github_repo/commits/$current_commit/"
160+
failed_body+="$submodule_name: ❌ Commits have DIVERGED from a common ancestor"$'\n'
161+
failed_body+=" TARGET (${{ inputs.base_ref }} branch): $github_repo/commits/$target_commit/"$'\n'
162+
failed_body+=" CURRENT (PR #${{ inputs.pr_number }} from ${{ inputs.head_ref }}): $github_repo/commits/$current_commit/"$'\n\n'
163+
else
164+
echo " Repository: $github_repo (unable to generate GitHub URLs)"
165+
echo " TARGET (${{ inputs.base_ref }} branch): $target_commit"
166+
echo " CURRENT (PR #${{ inputs.pr_number }} from ${{ inputs.head_ref }}): $current_commit"
167+
failed_body+="$submodule_name: ❌ Commits have DIVERGED from a common ancestor"$'\n'
168+
failed_body+=" TARGET (${{ inputs.base_ref }} branch): $target_commit"$'\n'
169+
failed_body+=" CURRENT (PR #${{ inputs.pr_number }} from ${{ inputs.head_ref }}): $current_commit"$'\n\n'
170+
fi
171+
else
172+
echo "❌ $submodule_name: Commits have NO COMMON ANCESTOR"
173+
echo " This indicates commits are from completely different repositories or history"
174+
echo ""
175+
echo "📊 For detailed commit inspection:"
176+
failed=1
177+
changed=1
178+
if [[ -n "$github_repo" && "$github_repo" == https://github.com/* ]]; then
179+
echo " TARGET (${{ inputs.base_ref }} branch): $github_repo/commits/$target_commit/"
180+
echo " CURRENT (PR #${{ inputs.pr_number }} from ${{ inputs.head_ref }}): $github_repo/commits/$current_commit/"
181+
failed_body+="$submodule_name: ❌ Commits have NO COMMON ANCESTOR"$'\n'
182+
failed_body+=" TARGET (${{ inputs.base_ref }} branch): $github_repo/commits/$target_commit/"$'\n'
183+
failed_body+=" CURRENT (PR #${{ inputs.pr_number }} from ${{ inputs.head_ref }}): $github_repo/commits/$current_commit/"$'\n\n'
184+
else
185+
echo " Repository: $github_repo (unable to generate GitHub URLs)"
186+
echo " TARGET (${{ inputs.base_ref }} branch): $target_commit"
187+
echo " CURRENT (PR #${{ inputs.pr_number }} from ${{ inputs.head_ref }}): $current_commit"
188+
failed_body+="$submodule_name: ❌ Commits have NO COMMON ANCESTOR"$'\n'
189+
failed_body+=" TARGET (${{ inputs.base_ref }} branch): $target_commit"$'\n'
190+
failed_body+=" CURRENT (PR #${{ inputs.pr_number }} from ${{ inputs.head_ref }}): $current_commit"$'\n\n'
191+
fi
192+
fi
193+
fi
194+
cd "$GITHUB_WORKSPACE"
195+
196+
done < <(git submodule status)
197+
198+
# Set outputs
199+
echo "failed=$failed" >> $GITHUB_OUTPUT
200+
echo "changed=$changed" >> $GITHUB_OUTPUT
201+
if [[ $changed -eq 1 ]]; then
202+
comment_body=""
203+
if [[ -n "$success_body" ]]; then
204+
comment_body+="### ✅ Submodules that are properly updated:"$'\n'
205+
comment_body+="$success_body"$'\n'
206+
fi
207+
if [[ -n "$failed_body" ]]; then
208+
comment_body+="### ❌ Submodules that need attention:"$'\n'
209+
comment_body+="$failed_body"
210+
fi
211+
echo "comment_body<<EOF" >> $GITHUB_OUTPUT
212+
echo "$comment_body" >> $GITHUB_OUTPUT
213+
echo "EOF" >> $GITHUB_OUTPUT
214+
fi
215+
216+
if [[ $failed -eq 1 ]]; then
217+
echo ""
218+
echo "❌ One or more submodules are not fast-forwarded"
219+
echo "Please ensure submodule commits are fast-forwards of the ${{ inputs.base_ref }} branch"
220+
exit 1
221+
fi
222+
223+
echo ""
224+
echo "✅ All submodules are properly fast-forwarded"
225+
226+
comment:
227+
name: Comment on PR
228+
needs: [check]
229+
runs-on: ubuntu-latest
230+
if: always() && needs.check.outputs.changed == '1'
231+
steps:
232+
- name: Comment on PR
233+
uses: actions/github-script@v7
234+
with:
235+
script: |
236+
const failed = '${{ needs.check.outputs.failed }}' === '1';
237+
const title = failed ?
238+
'## ❌ Submodule Fast-Forward Check Failed' :
239+
'## ✅ Submodule Fast-Forward Check Results';
240+
241+
const commentBody = `${title}
242+
243+
**Check based on commit:** ${{ inputs.head_sha }} (PR #${{ inputs.pr_number }} from \`${{ inputs.head_ref }}\`)
244+
245+
${{ needs.check.outputs.comment_body }}
246+
${failed ? 'Please ensure all submodule commits are fast-forwards of the ${{ inputs.base_ref }} branch before merging.' : 'All submodule changes look good! ✨'}`;
247+
248+
await github.rest.issues.createComment({
249+
issue_number: ${{ inputs.pr_number }},
250+
owner: context.repo.owner,
251+
repo: context.repo.repo,
252+
body: commentBody
253+
});

.github/workflows/cicd-main.yml

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,17 @@ jobs:
103103
104104
echo "test_level=$TEST_LEVEL" | tee -a "$GITHUB_OUTPUT"
105105
106+
submodule-check:
107+
name: Check submodule fast-forward
108+
needs: [pre-flight]
109+
if: github.event_name == 'pull_request'
110+
uses: ./.github/workflows/_submodule_check.yml
111+
with:
112+
base_ref: ${{ github.base_ref }}
113+
head_ref: ${{ github.head_ref }}
114+
pr_number: ${{ github.event.number }}
115+
head_sha: ${{ github.event.pull_request.head.sha }}
116+
106117
lint-check:
107118
name: Lint check
108119
needs: [pre-flight]
@@ -128,31 +139,18 @@ jobs:
128139
uv venv
129140
uv run --group dev pre-commit install
130141
uv run --group dev pre-commit run --all-files --show-diff-on-failure --color=always
131-
- name: Minimize uv cache
132-
run: uv cache prune --ci
133-
134-
mypy-check:
135-
name: Mypy check
136-
needs: [pre-flight]
137-
runs-on: ubuntu-latest
138-
steps:
139-
- name: Checkout repository
140-
uses: actions/checkout@v4
141-
- name: Install uv
142-
uses: astral-sh/setup-uv@v5
143-
with:
144-
version: "0.7.2"
145-
enable-cache: true
146-
prune-cache: false
147-
# Faster than uv python install since it caches python alongside runner
148-
- name: "Set up Python"
149-
uses: actions/setup-python@v5
150-
with:
151-
python-version-file: ".python-version"
152-
- name: Check mypy
142+
# TODO: this is a temporary check and should be removed once we have 100% correctness
143+
- name: Check if any files with zero errors not in whitelist
153144
run: |
154-
uv venv
155-
uv run --group test mypy nemo_rl examples
145+
missing_count=0
146+
for file in $(uv run --group dev pyrefly check $(git ls-files 'nemo_rl/**/*.py' 'examples/**/*.py' 'docs/*.py' 'tools/**/*.py') --output-format json | jq -r --slurpfile all_files <(git ls-files 'nemo_rl/**/*.py' 'examples/**/*.py' 'docs/*.py' 'tools/**/*.py' | jq -R -s 'split("\n")[:-1]') --arg pwd "$(pwd)/" '(.errors | group_by(.path) | map({(.[0].path | sub($pwd; "")): length}) | add // {}) as $error_counts | $all_files[0][] | . as $file | if ($error_counts[$file] // 0) == 0 then $file else empty end'); do
147+
if ! fgrep -q "$file" pyrefly.toml; then
148+
echo "File $file has zero errors but is not in pyrefly.toml in the 'project-includes' list. Please add it to this whitelist."
149+
((missing_count++))
150+
fi
151+
done
152+
153+
exit $missing_count
156154
- name: Minimize uv cache
157155
run: uv cache prune --ci
158156

@@ -210,8 +208,8 @@ jobs:
210208
UNIT_TEST_SCRIPT: |
211209
cd /opt/nemo-rl
212210
if [[ "${{ needs.pre-flight.outputs.test_level }}" =~ ^(L0|L1|L2)$ ]]; then
213-
uv run --no-sync bash -x ./tests/run_unit.sh --cov=nemo_rl --cov-report=term-missing --cov-report=json -m \"not mcore\"
214-
uv run --extra mcore bash -x ./tests/run_unit.sh --cov=nemo_rl --cov-report=term-missing --cov-report=json -m mcore
211+
uv run --no-sync bash -x ./tests/run_unit.sh --cov=nemo_rl -m \"not mcore\"
212+
uv run --extra mcore bash -x ./tests/run_unit.sh --cov=nemo_rl --cov-append --cov-report=term-missing --cov-report=json -m mcore
215213
else
216214
echo Skipping unit tests for docs-only level
217215
fi
@@ -309,8 +307,7 @@ jobs:
309307
(
310308
needs.pre-flight.outputs.test_level != 'none' &&
311309
needs.sphinx-build.result == 'success' &&
312-
needs.tests.result == 'success' &&
313-
(needs.mypy-check.result == 'success' || true)
310+
needs.tests.result == 'success'
314311
)
315312
)
316313
}}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
name: Stale-Close-Inactive-Issues-PRs
2+
on:
3+
schedule:
4+
- cron: "30 1 * * *"
5+
6+
jobs:
7+
close-issues:
8+
uses: NVIDIA-NeMo/FW-CI-templates/.github/workflows/_close_inactive_issue_pr.yml@v0.44.0
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
name: Community Bot
2+
3+
on:
4+
issues:
5+
types: [opened, edited, reopened, closed, deleted]
6+
issue_comment:
7+
types: [created, edited, deleted]
8+
9+
jobs:
10+
community-bot:
11+
uses: NVIDIA-NeMo/FW-CI-templates/.github/workflows/_community_bot.yml@v0.49.1
12+
secrets:
13+
GH_TOKEN: ${{ secrets.PAT }}

.pre-commit-config.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,9 @@ repos:
3535
files: '.*\/[^\/]*_[^\/]*\.md$'
3636
exclude: '^\.github/'
3737
types: [file]
38+
39+
- repo: https://github.com/facebook/pyrefly
40+
rev: 0.24.2
41+
hooks:
42+
- id: pyrefly-typecheck
43+
files: \.py$

0 commit comments

Comments
 (0)