Synchronisation nocturne : Main vers Dev
Cette compétence est lue par le bot de synchronisation automatique lors du workflow nightly-sync-main-to-dev. Elle contient toute la connaissance métier pour fusionner main dans dev, résoudre les conflits, itérer sur la CI et expédier la PR.
Phase 1 : Créer la branche de synchronisation et fusionner
Configuration de la branche
- Créer la branche
$BRANCHà partir deorigin/dev - Fusionner :
git merge origin/main -X theirs --no-edit - Si des conflits persistent (par ex. add/add), les résoudre en favorisant main
Préserver les additions spécifiques à Dev
Ne PAS remplacer en bloc tous les fichiers partagés par la version de main. Dev a des fonctionnalités pas encore dans main (nouvelles classes, nouveaux modules, nouveaux tests). La fusion préserve les additions non conflictuelles des deux côtés — n'intervenir que là où il y a un véritable conflit.
Détection de la chaîne de squash-merge
Dev développe souvent des fonctionnalités comme une chaîne de PRs (PR1 → PR2 → PR3) où chacune s'appuie sur la précédente. Quand PR1 est squash-mergée dans main, git voit la version squashée de main et les commits originaux de dev comme des changements sans lien. -X theirs prendra le code PR1 de main et supprimera silencieusement les améliorations de PR2/PR3 dans dev.
Après la fusion, vérifier ce motif :
- Pour chaque fichier où
-X theirsa résolu un conflit, exécutergit log --oneline origin/dev -- <file>pour voir si dev a des commits venus APRÈS le code que main apporte. - Si dev a des commits de suivi (corrections de bugs, refactors, extensions), favoriser la version de dev pour ces sections.
- Si le conflit est juste main apportant une copie propre de ce que dev a déjà (pas de suivi), la version de main va bien.
Vérification pratique : exécuter git diff origin/dev -- <file> sur les fichiers
en conflit. Si le code de dev a été retiré ou révertir, investiguer si la
version de dev est la plus évoluée.
Exemples réels de la PR #4291 :
emerging_optimizers.py: La version de main était PLUS complète — elle a squash-mergé les PRs de dev plus en a ajouté davantage.-X theirsétait correct.distrib_optimizer.py: Main a écrasé le support deGroupedQuantizedTensorde dev. Dû restaurer_is_distopt_quantized_paramet la boucle étendue_expand_quantized_param_shard_for_casttout en gardant les ajouts NVFP4 de main. Cela a nécessité une fusion chirurgicale combinant les sections des deux.
Insight clé : les chaînes de squash-merge peuvent aller DANS CHAQUE DIRECTION. Parfois main est en avant (elle a squash-mergé le travail de dev + plus), parfois dev est en avant (elle a des PRs de suivi). Toujours faire un diff dans les deux sens avant de décider quelle version favoriser.
Fichiers à remplacer par ceux de Main
Ces fichiers ont des conflits sémantiques connus où les versions de dev
référencent des args ou APIs que main a supprimés ou renommés. Prendre la
version de main avec git checkout origin/main -- <file> :
megatron/training/training.py— référence des args spécifiques à devmegatron/training/initialize.py— référence des args spécifiques à devmegatron/training/utils.py— référence des args spécifiques à devmegatron/training/datasets/data_samplers.py— référence des args spécifiques à devmegatron/core/optimizer/layer_wise_optimizer.py— signature du constructeur
Caveat pour TOUS les remplacements : Après avoir pris la version de main de n'importe quel fichier, vous DEVEZ exécuter la procédure de détection d'API Mismatch (voir ci-dessous) sur ce fichier. Prendre le code appelant de main tout en gardant les implémentations appelées de dev est la source numéro 1 des bugs de synchronisation.
IMPORTANT : Ne PAS prendre pyproject.toml, uv.lock, ou
docker/Dockerfile.ci.dev de main. Ces trois fichiers sont un triple
étroitement couplé — la commande uv sync du Dockerfile doit correspondre aux
groupes de dépendances dans pyproject.toml, et uv.lock doit être cohérent
avec les deux. Les versions de main manquent des dépendances spécifiques à dev
(par ex. fast-hadamard-transform, la bonne révision TransformerEngine) et le
flag --group no_pypi_wheels nécessaire pour les installer. Garder les
versions de dev de tous les trois fichiers.
IMPORTANT : .github/CODEOWNERS ne DOIT JAMAIS être modifié par le bot de
synchronisation en aucune circonstance. Le CODEOWNERS de dev est
intentionnellement différent de celui de main — ne pas prendre la version de
main, ne pas les fusionner, ne pas toucher au fichier. Si la fusion produit
un conflit ou un diff non-zéro contre origin/dev sur ce chemin, restaurer
la version de dev littéralement :
git checkout origin/dev -- .github/CODEOWNERS
Puis vérifier avec git diff origin/dev -- .github/CODEOWNERS — la sortie
doit être vide. Modifier CODEOWNERS déclenche des demandes d'examen parasites
et crée des conflits avec la gouvernance de l'équipe dev ; annuler un
changement CODEOWNERS après que la PR soit landed est douloureux.
NE JAMAIS éditer manuellement uv.lock. C'est un fichier de lock généré
automatiquement. S'il doit changer, il doit être régénéré avec uv lock
dans un conteneur CUDA (voir .claude/skills/build-and-test/SKILL.md).
Réconciliation des sources Git (pyproject.toml)
Après avoir gardé le pyproject.toml de dev, vérifier si main a ajouté des
sources git NOUVELLES à [tool.uv.sources] qui n'existent pas dans la version
de dev. Le code fusionné de main peut importer à partir de packages disponibles
seulement à des révisions git spécifiques.
- Faire un diff des sections
[tool.uv.sources]:git show origin/main:pyproject.tomlvsgit show origin/dev:pyproject.toml - Pour chaque source git dans main mais pas dev, l'ajouter au
pyproject.tomlde dev - Pour les sources dans les deux mais à des révisions différentes, vérifier si la révision de dev fonctionne. Si la révision de dev est cassée (erreurs de parsing TOML, classes manquantes que le code de main importe), prendre la révision de main à la place.
Exemples réels de la PR #4291 :
nvidia-resiliency-ext: Letorch.pyde main importeget_write_results_queuequi existait seulement dans la révision git épinglée de main, pas sur PyPI. Dû ajouter la source git de main au pyproject.toml de dev.nemo-run: La révision épinglée de dev avait une erreur de parsing TOML avec uv 0.7.2. Dû changer pour la révision de main.
Après toute modification à pyproject.toml, régénérer uv.lock dans un
conteneur CUDA :
docker run --rm -v $(pwd):/workspace nvcr.io/nvidia/pytorch:26.02-py3 \
bash -c "pip install uv==0.7.2 && cd /workspace && \
uv venv .venv --system-site-packages && uv sync --only-group build && uv lock"
# Nettoyer le .venv détenu par root :
docker run --rm -v $(pwd):/workspace nvcr.io/nvidia/pytorch:26.02-py3 \
bash -c "rm -rf /workspace/.venv"
Détection d'API Mismatch (audit post-fusion)
La fusion peut créer du code « Frankenstein » où les appelants de main utilisent les implémentations de dev (ou vice versa) avec des signatures de méthode différentes. Cela compile bien mais échoue à l'exécution.
Après la fusion, auditer les sites d'appel cross-boundary :
- Identifier les fichiers où la version de main a été prise (
-X theirsougit checkout origin/mainexplicite) - Pour chacun, trouver tous les sites d'appel externes : classes qu'il instancie, méthodes qu'il appelle sur les objets importés, fonctions d'autres modules qu'il invoque
- Vérifier que les noms de méthode, nombres de paramètres et signatures correspondent entre l'appelant et l'implémentation dans l'arbre fusionné
- Faire particulièrement attention aux modules « interface » (fichiers définissant les classes de base) — si main et dev ont évolué l'interface différemment, chaque appelant et implémenteur doit s'accorder
Exemples réels de la PR #4291 :
multi_latent_attention.py(main) appelaitoff_interface.group_commit()mais l'interface de dev avait seulementgroup_offload()— méthode renomméemamba_model.py(main) appelaitinit_chunk_handler(3 params)mais l'interface de dev nécessitait 6 params — signature étendue sur devmamba_model.pyappelaitmark_not_offloadable()mais dev avaitmark_not_offload()— méthode renomméebulk_offload()faisait.remove()après quebulk_offload_group()ait déjà.pop()d le même item — double suppression d'une liste
Détection pratique :
# Pour chaque fichier pris de main, trouver ce qu'il importe et appelle
grep -rn "from <module> import\|<module>\." megatron/
# Recouper avec les implémentations réelles dans l'arbre fusionné
Leçons spécifiques au fichier de fusion
Ces leçons ont été apprises de la PR #4291. Elles peuvent récurrer si les mêmes fichiers continuent à diverger :
gated_delta_net.py: Si la fusion crée du code appelant des méthodes helper non existantes (par ex._resolve_cu_seqlens), prendre la version de dev en bloc.model_chunk_schedule_plan.py: Attendre les imports manquants (par ex.CudaGraphScope) silencieusement supprimés lors de la résolution de conflit.fine_grained_activation_offload.py: Fichier interface critique utilisé par beaucoup d'appelants. Si main et dev ont des noms/signatures de méthode divergents, préférer l'implémentation de dev et patcher les appelants issus de main pour correspondre.distrib_optimizer.py: Dev peut avoir des abstractions de type plus larges (par ex._is_distopt_quantized_paramcouvrant à la fois FP8 et GroupedQuantizedTensor). Main peut se simplifier en vérifications de type explicites. Restaurer les abstractions de dev.
Traitement spécial : data_schedule.py
Main et dev ont des classes complètement différentes dans ce fichier :
- Main :
HybridCPDataLoaderWrapper(importé par letraining.pyde main) - Dev :
BasePackingScheduler,DpBalancedScheduler,DefaultDynamicCPScheduler,wrap_data_iterator,get_batch_on_this_rank_for_sequence_packing(importés parpretrain_gpt.pyet les tests)
Ne PAS prendre une version en bloc. Garder le fichier de dev et ajouter
la classe HybridCPDataLoaderWrapper de main (plus toutes les imports
manquantes comme BalancedCPScheduler, Any, List) à la fin.
Restaurer les fichiers supprimés
Comparer git ls-tree entre origin/main et HEAD pour trouver les fichiers
dans main qui manquent de l'arbre fusionné. Pour chacun :
- Restaurer si le code de main l'importe/référence et se briserait sans lui
(par ex.
hybrid_cp_schedule.pysidata_schedule.pyimporte de lui) - Ne PAS restaurer si dev l'a intentionnellement supprimé — vérifier
git log origin/dev -- <file>pour le commit de suppression pour comprendre l'intention - Dans le doute, vérifier si n'importe quel fichier dans l'arbre fusionné importe du fichier manquant. Si rien ne l'importe, sauter.
Formatage
Exécuter sur TOUS les fichiers Python modifiés (relatif à origin/dev), dans
cet ordre :
black(version 24,--config pyproject.toml)isort- L'ordre compte : black en premier, puis isort — l'ordre inverse peut annuler le travail d'isort
pylintsur les fichiersmegatron/core/modifiés — fixer les violations missing-docstring et line-too-long avant de pousser
Vérifications d'invariant pre-push
Avant chaque git push dans ce workflow (le push initial en Phase 1 ET chaque
fix-push en Phase 3), exécuter ces vérifications bash. Si une échoue, corriger
la condition et re-vérifier avant de pousser :
# 1. CODEOWNERS doit être identique à celui de dev.
if ! git diff --quiet origin/dev -- .github/CODEOWNERS; then
echo "ABORT: .github/CODEOWNERS differs from origin/dev. Restore with:"
echo " git checkout origin/dev -- .github/CODEOWNERS"
exit 1
fi
# 2. Le triple gestion-dépendances doit être identique à celui de dev.
for f in pyproject.toml uv.lock docker/Dockerfile.ci.dev; do
if ! git diff --quiet origin/dev -- "$f"; then
# pyproject.toml est autorisé à différer SEULEMENT pour la réconciliation
# des sources git (entrées nouvelles [tool.uv.sources] de main). Si vous
# l'avez volontairement édité pour cette raison, contourner cette
# vérification en re-exécutant avec $f sauté.
echo "WARNING: $f differs from origin/dev"
fi
done
La vérification CODEOWNERS est un HARD abort — ne jamais pousser si elle échoue.
Commit et push
Après que les vérifications d'invariant pre-push passent, committer tout et pousser la branche.
Phase 2 : Créer le draft PR
-
Titre :
chore: nightly sync main into dev ($DATE) -
Créer comme draft :
gh pr create --draft -
Le corps devrait inclure :
-
Résumé de ce qui a été synchronisé (nombre de commits depuis main)
-
Statistiques de changement de ligne Python uniquement, pour que les reviewers puissent évaluer la surface de code réelle (excluant JSON de valeur golden, uv.lock, etc.). Calculer avec :
git diff --numstat origin/dev...HEAD -- '*.py' \ | awk 'BEGIN{a=0;d=0} {a+=$1; d+=$2} END{ printf "Python lines: +%d / -%d across %d files\n", a, d, NR }'Inclure la ligne exacte (par ex.
Python lines: +1234 / -567 across 42 files) dans le corps de la PR pour que les reviewers la voient d'un coup d'œil. -
Liste des fichiers où la version de main a été prise sur la fusion
-
Liste des fichiers supprimés dans dev mais restaurés (et pourquoi)
-
La sortie du remerge-diff (
git show --remerge-diff HEADsur le commit de fusion) pour que les reviewers inspectent SEULEMENT les résolutions de conflits. Si la sortie est très longue, résumer les conflits par fichier et mettre le diff complet dans un bloc<details>collapsible. Si git est trop vieux pour--remerge-diff, noter la version git et décrire la stratégie de fusion utilisée à la place.
-
-
Sauvegarder le numéro de PR pour les phases ultérieures
-
Ajouter les labels
Run functional testsetRun MBridge testsà la PR immédiatement après création. Le labelRun functional testss'assure que/ok to testdéclenche la suite CI complète (tests unitaires- tests fonctionnels/intégration avec entraînement 100-step et comparaison
de valeur golden). Le label
Run MBridge testsdéclenche la suite de test MBridge. Sans ces labels, seulement un sous-ensemble léger s'exécute.gh pr edit <PR_NUMBER> --repo $REPO \ --add-label "Run functional tests" \ --add-label "Run MBridge tests"
- tests fonctionnels/intégration avec entraînement 100-step et comparaison
de valeur golden). Le label
Phase 3 : Itération CI
Architecture CI
Nemo_CICD_Testest un job de gate aval agrégeant les résultats des tests unitaires, tests d'intégration et autres. S'il échoue, investiguer les jobs amont dont il dépend — ne PAS déboguer la gate elle-même.- Les tests d'intégration (H100, GB200) peuvent être sautés pour les PRs
de non-mainteneurs. C'est attendu ; le gate
Nemo_CICD_Testéchouera en résultat. tests/unit_tests/conftest.pyimporte demegatron.training.training, donc un import cassé danstraining.py(ou n'importe quoi qu'il importe transitivement) se répercute pour faire échouer TOUS les suites de tests. Si chaque job de test échoue avec ImportError, vérifier d'abord la chaîne d'import de training.py.
Modèle d'exécution : une étape, pas de background
Vous exécutez dans UNE étape GitHub Actions. Le moment où vous arrêtez d'émettre des appels à outils, l'étape se termine et le conteneur runner est détruit. Tout processus background que vous avez démarré meurt avec. Il n'y a PAS de session persistante et PAS de wakeup futur. Voir le bloc « NO background tasks » du prompt du workflow pour la liste d'interdiction complète.
Règle pratique : chaque attente pour que CI se résolve est un SEUL appel Bash au premier plan qui se bloque inline jusqu'à ce que l'attente soit résolue.
La boucle Fix-Then-Retrigger
Deux boucles imbriquées. Ne PAS les confondre :
- La boucle externe est VOTRE séquence d'appels à outils (chaque itération :
un
/ok to test, un polling bloquant, peut-être un fix-and-push). Ce n'est PAS une boucle Bash. Elle avance parce que vous faites des appels à outils nouveaux. - La boucle interne est un seul appel Bash bloquant utilisant
while true; do ... sleep 120; done. Elle s'exécute pendant une itération de la boucle externe et se termine quand CI atteint un état terminal pour cette itération.
La boucle externe se termine SEULEMENT quand le gate de Phase 4 est satisfait.
Source de vérité : gh pr view <PR_NUMBER> --repo $REPO --json statusCheckRollup.
Ceci liste chaque vérification requise, y compris les contextes de statut
externes (CI GitLab, copy-pr-bot, etc.) que gh api .../actions/runs/.../jobs
ne montre PAS.
Itération boucle-externe (chaque itération est quelques appels à outils) :
-
latest_sha=$(git rev-parse HEAD)(un appel Bash). -
Poster
/ok to test $latest_shasur la PR :gh pr comment <PR_NUMBER> --repo $REPO --body "/ok to test $latest_sha" -
UN appel Bash bloquant. C'est la boucle interne. Copier ce template littéralement, en changeant seulement
REPOetPR:REPO='NVIDIA/Megatron-LM' PR='<PR_NUMBER>' # Noms appariés case-insensitively, ancrés au START du nom. EXEMPT='copy-pr-bot|is-not-external-contributor|greptile|coderabbit|codeowners|.*review|.*approval|codecov|coverage|build-docs|doc-build|readthedocs|sphinx' # Vérification sentinel qui nous dit que CI a complètement exécuté. # Mettre à jour si le job de gate agrégé est renommé. SENTINEL='Nemo_CICD_Test' while true; do # Normaliser CheckRun (.status / .conclusion) et StatusContext # (.state) entrées dans la même forme {name, status, conclusion}. rollup=$(gh pr view "$PR" --repo "$REPO" --json statusCheckRollup --jq ' .statusCheckRollup[] | [ (.name // .context // "?"), (if .__typename == "StatusContext" then (if (.state == "PENDING" or .state == "EXPECTED") then "IN_PROGRESS" else "COMPLETED" end) else (.status // "UNKNOWN") end), (if .__typename == "StatusContext" then (if .state == "SUCCESS" then "SUCCESS" elif (.state == "FAILURE" or .state == "ERROR") then "FAILURE" else "NEUTRAL" end) else (.conclusion // "UNKNOWN") end) ] | @tsv') # Sentinel : ne PAS déclarer vert jusqu'à ce que le gate agrégat CI ait # atteint un état terminal. Avant /ok to test déclenche la run, le # sentinel est absent ; tandis que CI s'exécute, il est IN_PROGRESS. sentinel_line=$(printf '%s\n' "$rollup" | awk -F'\t' -v s="$SENTINEL" '$1 == s') sentinel_status=$(printf '%s\n' "$sentinel_line" | awk -F'\t' 'NR==1 {print $2}') if [ "$sentinel_status" != "COMPLETED" ]; then echo "=== $(date -u) waiting for $SENTINEL (status: ${sentinel_status:-absent}) ===" sleep 120 continue fi # Classifier les vérifications non-exempts (liste exemption appliquée au NAME seulement). non_exempt=$(printf '%s\n' "$rollup" | awk -F'\t' -v p="^($EXEMPT)" 'tolower($1) !~ tolower(p)') failed=$(printf '%s\n' "$non_exempt" | awk -F'\t' '$2 == "COMPLETED" && $3 !~ /^(SUCCESS|SKIPPED|NEUTRAL)$/') pending=$(printf '%s\n' "$non_exempt" | awk -F'\t' '$2 != "COMPLETED"') if [ -n "$failed" ]; then echo "=== NON-EXEMPT FAILURES ===" printf '%s\n' "$failed" echo "RESULT=FAILURE" exit 0 fi if [ -n "$pending" ]; then # Sentinel est COMPLETED mais une vérification non-exempt est encore # pending — rare mais possible. Continuer à attendre ; ne pas # expédier. echo "=== $(date -u) sentinel done but non-exempt checks still pending ===" printf '%s\n' "$pending" sleep 120 continue fi echo "=== ALL NON-EXEMPT CHECKS COMPLETED GREEN ===" printf '%s\n' "$non_exempt" echo "RESULT=GREEN" exit 0 doneCet appel Bash se bloque aussi longtemps que CI prend (minutes à heures). Ne PAS le scinder en beaucoup de courts polls entrelacés avec d'autres appels à outils — ça gaspille
--max-turnset crée des fenêtres où vous pourriez perdre la trace de l'état de la boucle. -
Lire la sortie de l'outil :
- Si
RESULT=FAILURE: utilisergh api repos/$REPO/actions/jobs/<JOB_ID>/logs(ou l'équivalent pour les contextes externes) pour diagnostiquer, fixer le code, committer, pousser. Puis démarrer une NOUVELLE itération boucle-externe à l'étape 1 avec le nouveau HEAD SHA. - Si
RESULT=GREEN: la boucle externe est terminée. Procéder à Phase 4.
- Si
Pourquoi pas attendre la run à s'enregistrer d'abord ? gh pr comment avec
/ok to test <sha> est géré par copy-pr-bot, qui prend quelques secondes
pour déclencher la run CI. Le poll statusCheckRollup à l'étape 3 montrera
initialement les vérifications en PENDING / QUEUED ; c'est bon — la boucle
interne traite ça comme « continuer l'attente » et les verra avancer comme CI
progresse. Pas de poll d'enregistrement séparé nécessaire.
Anti-motifs (ce qui s'est mal passé à la run 24800621116)
- Ne PAS classifier un job en queue/in-progress comme « infrastructure- blocked » et expédier. Une queue bloquée se vide finalement — attendre. Si le job finit par passer, parfait ; s'il échoue, aller le fixer.
- Ne PAS marquer prêt tandis qu'une vérification requise est
PENDING/QUEUED/IN_PROGRESSsur le HEAD SHA. Un push n'est pas un pass ; seulement unCOMPLETED+ statut vert l'est. - Ne PAS déclarer un job untested « pre-existing ». Pre-existing signifie que le test a exécuté jusqu'à complétion et a échoué de la même manière sur la CI dev récente. Un job qui n'a jamais exécuté sur votre PR ne peut pas être pre-existing.
- Ne PAS utiliser
gh api .../actions/runs/.../jobsseul comme signal de gate. Les contextes de statut externes (pipelines GitLab CI, statutcopy-pr-bot, etc.) N'APPARAISSENT PAS là. UtiliserstatusCheckRollup. - Ne PAS démarrer aucun processus background. Pas de
&, pas denohup, pas derun_in_background: true, pas deScheduleWakeup. L'étape GitHub Actions possède votre shell ; quand l'étape finit, chaque processus background est tué et ne peut pas reprendre. - Ne PAS pousser directement vers les branches
pull-request/<PR_NUMBER>. Le bot communautaire gère ces branches quand il traite/ok to test. Pousser vers elles directement casse le mécanisme de déclenchement CI. Toujours pousser vers votre propre branche de sync (par ex.main2dev/<DATE>) à la place. - Ne PAS oublier les labels
Run functional testsetRun MBridge tests. SansRun functional tests, les tests fonctionnels GitLab internes n'exécutent pas ; sansRun MBridge tests, la suite de test MBridge n'exécute pas.
Investigation des défaillances
- Récupérer les logs :
gh api repos/$REPO/actions/jobs/<JOB_ID>/logs - Grepper pour :
ImportError,ModuleNotFoundError,FAILED,would reformat,line-too-long,Traceback - Lire l'erreur, comprendre la cause racine, fixer le code
Problèmes courants
- ImportError pour une classe/module : Le test de dev importe une classe d'un fichier où nous avons pris la version de main. Restaurer seulement la classe/fonction manquante — pas le fichier entier. Si les classes d'un fichier sont complètement différentes entre main et dev, garder les deux ensembles de code.
- Défaillances de formatage (black/pylint) : Exécuter
black --config pyproject.tomlsur les fichiers offensants. Pour pylint long-line ou missing-docstring, éditer directement. - Imports circulaires :
isortpeut réordonner les imports d'une manière qui introduit des dépendances circulaires (par ex.megatron/legacy/model/__init__.py). Vérifiergit diffsur les fichiers__init__.pypour voir si l'ordre d'import a changé. - Incompatibilités de version de dépendance : Prendre le
pyproject.toml/uv.lockde main peut changer les versions de bibliothèque dans le conteneur CI. Le code spécifique à dev peut dépendre de versions plus récentes (par ex.single_grouped_weightde TransformerEngine). Si les défaillances tracent des kwargs manquants ou des APIs changées dans les libs tiers, c'est la cause. - API mismatch (AttributeError / TypeError à l'exécution) : Les appelants de main référencent des méthodes qui n'existent pas (ou ont des signatures différentes) dans les implémentations de dev. Voir « Détection d'API Mismatch » en Phase 1. Fixer en ajoutant des shims, renommant les méthodes, ou ajustant les signatures d'appel.
- Défaillances infrastructure / réseau (apt-get, pip download) : Les
erreurs comme
archive.ubuntu.com unreachableouConnection timed outlors de l'installation de packages sont des problèmes transients d'infrastructure CI, pas de code. Réessayer CI avec le même SHA. Ne pas investiguer comme des défaillances de code.
Vérification de défaillance pre-existing
Vous DEVEZ empiriquement vérifier avant de classifier une défaillance comme pre-existing.
gh pr list --repo $REPO --base dev --state merged --limit 3gh pr checks <PR_NUMBER> --repo $REPOsur une PR dev récemment mergée- Si le même bucket de test passe sur la CI dev récente → la défaillance est causée par la sync. Vous devez la fixer.
- Seulement si le test échoue aussi sur la CI dev récente pouvez-vous la classifier comme pre-existing. Documenter avec le numéro de PR de dev et la CI run en tant que preuve.
Tests fonctionnels internes GitLab
La CI GitHub couvre les tests unitaires et certains tests d'intégration. Le
GitLab interne (gitlab-master.nvidia.com) exécute des tests fonctionnels
supplémentaires sur le matériel H100/GB200 qui peuvent révéler des problèmes
que la CI GitHub ne détecte pas. Ceux-ci apparaissent dans statusCheckRollup
comme des contextes de statut externes (le template bash les gère déjà via la
branche __typename == "StatusContext").
- Par exemple, les défaillances d'offload d'activation fine-grained ont seulement montré dans les tests fonctionnels GitLab lors de la PR #4291
- Si la CI GitHub passe mais qu'un reviewer signale des défaillances GitLab, investiguer avec la même rigueur que les défaillances CI GitHub
- La PR de sync devrait idéalement passer à la fois la CI GitHub et GitLab
avant merge, mais la CI GitHub qui passe (i.e. le gate Phase 4 ci-dessous)
est le minimum avant
gh pr ready
Phase 4 : Marquer PR Ready — Gate strict
Exécuter gh pr ready SEULEMENT quand chaque vérification requise non-exempt
sur la dernière run CI (contre le HEAD SHA actuel) satisfait BOTH :
status == "completed"— NIqueued,in_progress,pending,waiting, ourequested.conclusion ∈ {"success", "skipped", "neutral"}.
Si une vérification non-exempt est pending/queued/in-progress : continuer le
polling ; ne pas exécuter gh pr ready. Si elle échoue : revenir à la boucle
Phase 3.
La liste exemption (approval/coverage/docs) est définie en Phase 3 ; seulement ces vérifications peuvent être ignorées.
Une défaillance pre-existing (même test échouant identiquement sur la CI dev récente) peut être acceptée, mais SEULEMENT après qu'elle ait complètement exécuté, été empiriquement vérifiée contre dev, et documentée dans le corps de la PR avec preuve (numéro de PR de dev + URL de CI run).
gh pr ready <PR_NUMBER> --repo $REPO
Puis commenter sur la PR confirmant qu'elle est prête pour l'examen humain. Le commentaire devrait inclure :
- Quelles vérifications non-exempt ont passé (résumé de la sortie de template
bash finale
ALL NON-EXEMPT CHECKS COMPLETED GREEN) - Toute défaillance pre-existing documentée avec preuve (numéro de PR de dev
- URL de CI run montrant la même défaillance sur la CI dev récente)
- Quels fichiers ont été pris de main vs. mergés manuellement
- Tout API mismatch détecté et fixé
- Toute réconciliation de source git
pyproject.tomleffectuée - Liens vers les CI runs qui ont validé les fixes
Règles
- Prioriser main sur dev sur les conflits authentiques. Préserver les additions spécifiques à dev qui ne conflictent pas.
- Les triggers CI via commentaire :
/ok to test <sha> - Les runs CI apparaissent sur la branche
pull-request/<PR_NUMBER> - Identité du committer git :
svcnvidia-nemo-ci - Après édition d'imports, exécuter
isortsur ces fichiers - Pousser directement vers NVIDIA/Megatron-LM (pas un fork). Le bot utilise un PAT avec accès en écriture. CLAUDE.md dit « ne jamais pousser directement » mais cette règle est pour les contributeurs humains — le bot de sync est une exception.