with one click
rebase-mlflow
Rebase the ODH MLflow fork onto a new upstream MLflow release tag
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
Rebase the ODH MLflow fork onto a new upstream MLflow release tag
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
| name | rebase-mlflow |
| description | Rebase the ODH MLflow fork onto a new upstream MLflow release tag |
| allowed-tools | ["Read","Bash","Edit","Write","Agent","AskUserQuestion","TaskCreate","TaskUpdate","TaskList"] |
| argument-hint | <upstream_tag> (e.g. v3.14.0) |
| arguments | ["upstream_tag"] |
Rebase the OpenDataHub MLflow downstream fork onto an upstream MLflow release.
Reference: RHOAIENG-61516
History: See FORK_HISTORY.md in the repo root for past rebase records.
The ODH MLflow repo is a fork of upstream mlflow/mlflow. We carry ~20 downstream commits (PatternFly CSS overrides, module federation, Konflux build configs, feature flags, E2E tests, etc.) on top of an upstream release tag.
When upstream cuts a new release, we need to reapply our changes on top of it. This skill walks through that process in 8 phases:
| Phase | What happens | Time estimate |
|---|---|---|
| 1. Preparation | Fetch, back up, inventory commits | ~10 min |
| 2. Squash | Group 20+ keep commits into 3 clean commits | ~15 min |
| 3. Rebase | git rebase --onto the target tag, resolve conflicts | ~30 min |
| 4. CI fixes | Fix recurring issues + cherry-pick upstream test fixes | ~30 min |
| 5. Merge strategy | git merge -s ours to link histories (no force push) | ~2 min |
| 6. Documentation | Update FORK_HISTORY.md | ~10 min |
| 7. CI validation | Push, create PR, wait for ~70 CI jobs, fix failures | ~2 hours |
| 8. Real PR | Create the actual merge PR, get reviews | ~1 day |
You will need: Push access to opendatahub-io/mlflow and the following remotes:
mlflow → https://github.com/mlflow/mlflow.git (upstream source)
upstream → https://github.com/opendatahub-io/mlflow.git (ODH fork)
origin → your personal fork
Before starting, verify:
opendatahub-io/mlflow.Goal: Understand what we're working with — the current base, all downstream commits, and which can be dropped.
Fetch all remotes and tags:
git fetch mlflow --tags
git fetch upstream
Identify the current base version (the upstream tag ODH master is built on):
git log --oneline upstream/master | grep 'Bump version to' | head -1
This gives you CURRENT_VERSION (e.g., v3.12.0). You'll use this throughout.
Back up master (naming convention: master-MM-DD):
git push upstream upstream/master:refs/heads/master-$(date +%m-%d)
Verify the backup exists: git ls-remote upstream refs/heads/master-$(date +%m-%d)
Inventory all downstream commits:
git log --format='%h %s' upstream/master --not $CURRENT_VERSION
Categorize each commit:
drop: — cherry-picks from upstream. Drop if the original is in the target tag.backport: — treat as drop: if the original commit (check cherry picked from trailer) is in the target tag.keep: — ODH-specific changes to preserve.keep: commits.Verify all drops are in the target tag:
git log --oneline $UPSTREAM_TAG --grep="<PR number>"
Check files touched by each keep commit to assign it to a category:
for hash in <keep_commit_hashes>; do
echo "=== $(git log --format='%h %s' -1 $hash) ==="
git diff-tree --no-commit-id --name-only -r $hash | head -20
echo ""
done
Next: You now have a list of commits to keep, grouped by category. Move to Phase 2.
Goal: Produce a branch with exactly 3 clean commits on top of CURRENT_VERSION.
Squash keep: commits into 3 categories, in this order:
| Category | What goes here |
|---|---|
| Fork scaffolding | GitHub workflows, Dockerfiles, Tekton, OWNERS, Konflux configs, .syft.yaml, requirements/konflux-*, requirements/rpms.* |
| Backend changes | Python code, uv.lock, requirements/ (non-Konflux) |
| UI changes | Everything under mlflow/server/js/, CSS overrides, module federation, E2E tests, src/odh/ |
Why squash? 20+ commits = 20+ conflict rounds during rebase. 3 commits = at most 3. Originals are preserved in the backup branch and in squash commit messages.
Create the rebase branch:
git checkout -b rebase-$UPSTREAM_TAG $CURRENT_VERSION
Cherry-pick each category using --no-commit to squash:
# 1. Scaffolding (oldest first)
git cherry-pick --no-commit <hash1> <hash2> ...
git commit -s -m "keep: Fork scaffolding (squashed)
Squashed from:
- <hash> <subject>
..."
# 2. Backend
git cherry-pick --no-commit <hash1> ...
git commit -s -m "keep: Backend changes (squashed)
Squashed from:
- <hash> <subject>
..."
# 3. UI
git cherry-pick --no-commit <hash1> <hash2> ...
git commit -s -m "keep: UI changes (squashed)
Squashed from:
- <hash> <subject>
..."
If you hit conflicts during squashing: Later commits may conflict with earlier ones in the same category (e.g., one creates a file, another modifies it). Resolve by taking the final version from upstream/master:
git show upstream/master:<conflicted-file> > <conflicted-file>
git add <conflicted-file>
Next: You have a branch with 3 squashed commits on CURRENT_VERSION. Move to Phase 3 to rebase them onto the target tag.
Goal: Move the 3 squashed commits from CURRENT_VERSION to the target tag, resolving all merge conflicts.
Rebase:
git rebase --onto $UPSTREAM_TAG $CURRENT_VERSION rebase-$UPSTREAM_TAG
Resolve conflicts. Git will stop at each conflicted commit. See the Conflict Resolution Reference at the bottom for file-by-file patterns. The general rules:
git rmAfter resolving each commit's conflicts:
git add -A
GIT_EDITOR=true git rebase --continue
Verify the rebase:
# No stray conflict markers
git diff $UPSTREAM_TAG..rebase-$UPSTREAM_TAG -- . | grep -c '<<<<<<<'
# TypeScript compiles
cd mlflow/server/js && npx tsc --noEmit --pretty
# ODH-specific files exist
ls OWNERS Dockerfile.konflux mlflow/server/js/src/odh/
# Key ODH features present
grep -n 'MLFLOW_ENABLE_ASSISTANT\|MLFLOW_ENABLE_AI_GATEWAY' mlflow/environment_variables.py
grep -n '_disable_gateway' mlflow/server/handlers.py | head -3
grep -n 'onWorkspaceChange' mlflow/server/js/src/workspaces/utils/WorkspaceUtils.ts
Next: The rebase is done but CI will fail on several known issues. Move to Phase 4 to fix them before pushing.
Goal: Fix 5 issues that break CI on every rebase because upstream changes reset ODH-specific config.
i18n key drift — Conflict resolution keeps i18n entries from both sides, but some upstream keys reference components removed in the same release:
cd mlflow/server/js && yarn i18n
git diff src/lang/default/en.json # confirm only orphaned keys removed
UV_EXCLUDE_NEWER env var — .github/actions/setup-python/action.yml hardcodes UV_EXCLUDE_NEWER. Upstream sets P7D; ODH needs P14D. The env var overrides pyproject.toml, causing dev/pyproject.py → uv lock to regenerate uv.lock with the wrong span, failing the version-sync CI check:
# Verify — must say P14D, not P7D
grep 'UV_EXCLUDE_NEWER' .github/actions/setup-python/action.yml
uv version pinning — Same action file pins a uv version. If upstream bumped it, verify compatibility with pyproject.toml's required-version. Mismatches cause uv lock output to differ between local and CI.
Conftest lint for composite actions — Repo policy forbids ${{ }} interpolation in run: blocks of composite actions. ODH files (e.g., .github/actions/build-image/action.yml) may violate this. Move values to env: blocks:
# Bad
run: echo ${{ inputs.FOO }}
# Good
env:
FOO: ${{ inputs.FOO }}
run: echo "$FOO"
Verify: uv run pre-commit run conftest --all-files
FORK_HISTORY.md typos exclusion — FORK_HISTORY.md is excluded from the typos checker via [tool.typos.files] extend-exclude in pyproject.toml (git hashes trigger false positives). If lost during a rebase conflict, re-add "FORK_HISTORY.md" to the list.
Prettier formatting — The pre-commit hook uses prettier v2 (pinned in .pre-commit-config.yaml), NOT the latest v3. Do NOT use npx prettier (which installs v3 and formats differently). Always use the pre-commit hook:
uv run pre-commit run prettier --files FORK_HISTORY.md .claude/skills/rebase-mlflow/skill.md
Upstream tests broken by ODH simplifications — When conflict resolution simplifies upstream behavior (e.g., validations.ts catch-all), the corresponding upstream tests may still expect the old behavior. Run the JS tests locally to catch these:
cd mlflow/server/js && yarn test --watchAll=false 2>&1 | tail -20
Update the test expectations to match ODH's implementation, not the other way around.
Commit all ODH-specific fixes together:
git add -A && git commit -s -m "keep: Post-rebase CI fixes
- Run yarn i18n to remove orphaned keys
- Set UV_EXCLUDE_NEWER=P14D in setup-python action
- Fix conftest lint violations in composite actions
- Verify FORK_HISTORY.md typos exclusion in pyproject.toml
- Run prettier on markdown files
- Update tests broken by ODH simplifications"
Cherry-pick upstream flaky test fixes — v3.13.0 may include tests that fail due to bugs fixed on upstream master after the release. These cause CI failures on every PR if not addressed. Check upstream for fixes:
git fetch mlflow master
# Search for fixes to failing test files
git log --oneline mlflow/master --not $UPSTREAM_TAG -- <failing-test-file>
Cherry-pick carefully — avoid full uv.lock regenerations. For commits that bundle test fixes with lock file changes, apply only the test file changes manually:
# Safe: cherry-pick commits that only touch test files
git cherry-pick --no-commit <hash>
# For commits that also update uv.lock: apply only the test fix
sed -i 's/old_pattern/new_pattern/' <test-file>
Commit with drop: prefix (these get removed on the next rebase):
git commit -s -m "drop: Cherry-pick upstream test fixes for CI stability
Cherry-picked from upstream master (post-$UPSTREAM_TAG):
- <hash> <subject>"
Common upstream flaky tests to check for fixes:
tests/assistant/test_tool_executor.py — asyncio.get_event_loop() → asyncio.run()tests/gateway/test_tracing_utils.py — same asyncio patterntests/data/test_huggingface_dataset_and_source.py — HF Hub rate limits / network issuestests/webhooks/test_e2e.py — server startup timeout too shortNext: CI issues are fixed. Move to Phase 5 to prepare the branch for merging.
Goal: Make the rebase branch mergeable to master via a normal PR, without force pushing.
Product security prohibits force pushing. Use the merge -s ours strategy:
Link histories:
git merge -s ours upstream/master -m "Automated sync: integrating upstream MLflow $UPSTREAM_TAG"
This creates a merge commit that keeps our rebased content bit-for-bit but adds upstream/master as an ancestor. GitHub now sees master as reachable from our branch, so a PR can be merged normally.
If new PRs merge to master during the rebase process, run git merge -s ours upstream/master again on the rebase branch. This is safe to repeat — it just adds another merge commit linking the latest master. Update FORK_HISTORY.md to note which PRs were included. Then rebuild the ci-check branch.
Next: Move to Phase 6 to document what was done.
Goal: Record everything in FORK_HISTORY.md so the next rebase has full context.
Add a rebase entry to FORK_HISTORY.md documenting:
keep:/drop: prefixKeep updating FORK_HISTORY.md throughout the process — don't wait until the end. Every fix you make after the initial rebase (CI failures, UI regressions, test mismatches, upstream cherry-picks) should be added.
Commit:
git add FORK_HISTORY.md .claude/skills/rebase-mlflow/skill.md && \
git commit -s -m "keep: Post-rebase fixes and documentation"
Combine the CI fixes, test fixes, documentation, and skill into a single keep: commit. The upstream cherry-picks stay as a separate drop: commit.
Next: The branch is ready. Move to Phase 7 to validate with CI.
Goal: Run CI against the rebase branch to catch issues before the real PR. Most relevant workflows trigger naturally; optionally remove path filters for broader coverage (skip slow-tests.yml and helm.yml — they test upstream-only features).
Push the clean rebase branch:
git push upstream rebase-$UPSTREAM_TAG
Create a CI check branch with workflow path filters removed so all jobs fire:
git checkout -b ci-check-rebase-$UPSTREAM_TAG rebase-$UPSTREAM_TAG
python3 -c "
import re, glob
for wf in sorted(glob.glob('.github/workflows/*.yml')):
with open(wf) as f: content = f.read()
original = content
content = re.sub(r'(\n paths(?:-ignore)?:\n(?: - .*\n)+)', '\n', content)
if content != original:
with open(wf, 'w') as f: f.write(content)
"
git add .github/workflows/
git commit -s -m "TEMPORARY: Remove workflow path filters for full CI validation
DO NOT MERGE - this commit exists only to trigger all CI workflows."
git push upstream ci-check-rebase-$UPSTREAM_TAG
Create the CI validation PR — NOT as a draft. Include collaboration instructions in the body so teammates know how to contribute fixes:
gh pr create --repo opendatahub-io/mlflow --base master \
--head ci-check-rebase-$UPSTREAM_TAG \
--title "[DO NOT MERGE] CI validation for MLflow $UPSTREAM_TAG rebase" \
--body "$(cat <<'EOF'
## DO NOT MERGE THIS PR
CI validation only for the MLflow $UPSTREAM_TAG rebase. Workflow path filters removed to trigger all pipelines.
**Clean rebase branch:** `rebase-$UPSTREAM_TAG`
### Want to help test or fix something?
If you spot an issue on the rebased branch, here's how to submit a fix:
1. Create a branch off `rebase-$UPSTREAM_TAG` (not master)
2. Commit your fix with a `keep:` prefix (e.g. `keep: Fix modal padding after rebase`)
3. Add a line to `FORK_HISTORY.md` under "Post-rebase fixes" describing what you fixed
4. Open a PR targeting `rebase-$UPSTREAM_TAG` (not master)
The rebase owner will merge it and rebuild the CI validation branch.
EOF
)"
Do NOT use --draft. Most workflows have if: draft == false and will skip. Un-drafting later does not re-trigger them (the ready_for_review event is not in the triggers).
Monitor CI: gh pr checks <PR_NUMBER> --repo opendatahub-io/mlflow
If CI fails: Always fix on the clean branch first, then rebuild ci-check:
# 1. Fix on the clean branch
git checkout rebase-$UPSTREAM_TAG
# ... make fixes, commit ...
git push upstream rebase-$UPSTREAM_TAG
# 2. Rebuild ci-check: reset to clean branch, re-apply workflow removal
git checkout ci-check-rebase-$UPSTREAM_TAG
git reset --hard rebase-$UPSTREAM_TAG
python3 -c "
import re, glob
for wf in sorted(glob.glob('.github/workflows/*.yml')):
with open(wf) as f: content = f.read()
original = content
content = re.sub(r'(\n paths(?:-ignore)?:\n(?: - .*\n)+)', '\n', content)
if content != original:
with open(wf, 'w') as f: f.write(content)
"
git add .github/workflows/
git commit -s -m "TEMPORARY: Remove workflow path filters for full CI validation
DO NOT MERGE - this commit exists only to trigger all CI workflows."
git push upstream ci-check-rebase-$UPSTREAM_TAG --force-with-lease
This keeps ci-check as exactly rebase-$UPSTREAM_TAG + one disposable commit.
Close the PR once all CI passes (do NOT merge).
Next: CI is green. Move to Phase 8 to create the real PR.
Goal: Get the rebase reviewed and merged into master.
Create the real PR. The repo requires ## Upstream / Downstream Impact and ## Testing sections in the PR body:
gh pr create --repo opendatahub-io/mlflow --base master \
--head rebase-$UPSTREAM_TAG \
--title "Rebase ODH MLflow onto upstream $UPSTREAM_TAG"
Include in the body: summary of commits, what changed, regression check results, and the required template sections:
## Upstream / Downstream Impact
- [x] Downstream-only change for `opendatahub-io/mlflow`
## Testing
- [x] CI
- [x] Unit tests
- [x] Manual testing
Get reviews, then merge. The merge -s ours commit ensures the PR diff is clean.
Sync downstream — after merging, notify the team to sync red-hat-data-services/mlflow from midstream before any code freeze deadlines.
Cleanup after merge:
ci-check-rebase-$UPSTREAM_TAG branch from upstreamrebase-$UPSTREAM_TAG and master-MM-DD for referenceTeammates can help test and fix the rebase branch without running this skill.
For teammates submitting fixes:
rebase-$UPSTREAM_TAG (not master)keep: prefixFORK_HISTORY.md under "Post-rebase fixes" describing what was fixed and whyrebase-$UPSTREAM_TAG (not master)For the rebase owner (the person running this skill):
rebase-$UPSTREAM_TAGgit pull upstream rebase-$UPSTREAM_TAGgit push upstream ci-check-rebase-$UPSTREAM_TAG --force-with-leaseThis keeps the rebase owner in control of the ci-check branch and the final merge, while letting the team contribute fixes in parallel.
Priority order:
onWorkspaceChangeexclude-newer-span, accept upstream depsgit show upstream/master:<file> for the intended final stateScaffolding commit:
| File | Resolution |
|---|---|
.github/workflows/*.yml (modify/delete) | ODH deletes upstream workflows (approval, autoformat, cross-version-tests, slow-tests, helm, etc.). git rm all. |
.github/workflows/master.yml | Remove Databricks-specific test steps. |
pyproject.toml [tool.uv] | Keep ODH's exclude-newer = "P14D" and extra exclude-newer-package entries. Take upstream's required-version. |
uv.lock | Take ODH's exclude-newer-span = "P14D". |
UI commit:
| File | Resolution |
|---|---|
mlflow/environment_variables.py | Keep both upstream's new vars and ODH's MLFLOW_ENABLE_ASSISTANT / MLFLOW_ENABLE_AI_GATEWAY. |
mlflow/server/handlers.py | Keep ODH's _disable_gateway decorator. Accept upstream function renames. |
craco.config.js | Keep both webpack config functions (upstream's + ODH's suppressAutoprefixerWarnings). |
MlflowRouter.tsx | Keep upstream route imports + ODH's shouldEnableAIGateway() wrapper on gateway routes. |
MlflowSidebar.tsx | Keep upstream imports + ODH's isAssistantEnabled. |
validations.ts | Keep ODH's simplified .catch(() => callback(undefined)). Remove dead isResourceDoesNotExistError and unused ErrorCodes import. |
ExperimentViewHeader.tsx | Keep imports from both sides. |
en.json | Keep entries from both sides in alphabetical order. Then run yarn i18n in Phase 4 to prune orphans. |
WorkspaceUtils.ts | Keep both upstream's useSyncExternalStore pattern AND ODH's onWorkspaceChange. Both listener sets must be notified in setActiveWorkspace. |
Run through this before creating the real PR:
Rebase integrity:
master-MM-DDdrop:/backport: commits confirmed in target taggrep -c '<<<<<<<')Recurring CI fixes applied:
yarn i18n)UV_EXCLUDE_NEWER set to P14D in .github/actions/setup-python/action.ymluv version pin compatible with pyproject.toml required-versionFORK_HISTORY.md excluded from typos checker in pyproject.tomluv run pre-commit run prettier)Upstream test fixes:
drop: prefixDocumentation and validation:
FORK_HISTORY.md updated with full changelog (conflicts, fixes, cherry-picks)rebase-$UPSTREAM_TAG branch (not only on ci-check)red-hat-data-services/mlflow after merge