fix: stop InternalCompat requesting an Internal version that was never published - #2655
fix: stop InternalCompat requesting an Internal version that was never published#2655Rana Singh (ranadeepsingh) wants to merge 2 commits into
Conversation
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
|
Hey Rana Singh (@ranadeepsingh) 👋! We use semantic commit messages to streamline the release process. Examples of commit messages with semantic prefixes:
To test your commit locally, please follow our guild on building from source. |
There was a problem hiding this comment.
Pull request overview
This PR fixes the InternalCompat CI failure mode where SynapseML-Internal’s sbt-dynver version drifts between steps (due to dirty working tree + wall-clock HHmm suffix), causing Internal Python tests to request a Maven coordinate that was never published.
Changes:
- Commit the
build.sbtretarget edits during the InternalCompat job so sbt-dynver produces a stable version across later, separate JVM invocations. - Add a guard that fails loudly if the Internal repo remains dirty after the commit (preventing silent reintroduction of version drift).
- Improve diagnostics by globbing Internal artifacts in
~/.m2across Scala suffixes (rather than hardcoding_2.12).
Show a summary per file
| File | Description |
|---|---|
pipeline.yaml |
Stabilizes the Internal build/version across CI steps by committing retarget edits and improves artifact diagnostics for Scala 2.13 branches. |
Review details
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Files reviewed: 1/1 changed files
- Comments generated: 1
- Review effort level: Lite
| git -c user.email=synapseml-ci@microsoft.com -c user.name='SynapseML CI' \ | ||
| commit --quiet --no-verify -am "CI: retarget to OSS ${OSS_VERSION}" | ||
|
|
There was a problem hiding this comment.
Good catch - this was a real defect in the guard, and I verified it rather than assuming:
git commit --quiet --no-verify -am (nothing staged) -> exit 1
git diff-index --quiet HEAD -- (clean tree) -> exit 0
git diff-index --quiet HEAD -- (dirty tree) -> exit 1
The step runs under set -e (top of the same script), so an unconditional commit would have turned the already retargeted case into a hard step failure and made the step non-idempotent on retry.
Fixed in 7be7e32 by committing only when the tree is actually dirty. The preceding grep -qF checks already prove build.sbt holds the requested synapseMLVersion and Resolver.mavenLocal, so an unchanged tree is a valid state rather than an error - and either way dynver sees a clean tree and emits one stable version, which is the property the rest of the job depends on. The post-commit diff-index guard is unchanged and still fails loudly if the tree is dirty at that point.
…r published 'Run Internal Python tests (ExcludeAIFunc)' fails on essentially every PR. All ~21 tests error at setup and the only visible cause is an opaque JAVA_GATEWAY_EXITED, which hides an Ivy resolve failure for an Internal jar that no step ever published. Internal's version comes from sbt-dynver, which appends a live '-<yyyyMMdd>-<HHmm>' suffix whenever the working tree is dirty and recomputes it from the wall clock on every sbt load. The 'Retarget Internal to this build' step edits build.sbt with two sed commands, which is exactly what makes the tree dirty for the rest of the job. Each later sbt session therefore picks a different version. Observed on build 231488245 (PR #2645): 11:32 'sbt packagePython publishM2' bakes ...-1132-SNAPSHOT into the generated Python package and publishes that same jar to ~/.m2 11:36 'sbt testPythonExcludeAIFunc' re-runs CodeGen in a new sbt session, rebakes the package as ...-1136-SNAPSHOT and pip-installs it 11:37 pytest fixtures read the baked coordinate out of the installed package via synapse.ml.ebm.__spark_package_version__ and ask Ivy for ...-1136-SNAPSHOT, which nobody published make_mlflow_models.py runs in between and succeeds: it resolves the 1132 jar correctly from local-m2-cache, because the rebake has not happened yet. The existing same-session mitigation for packagePython and publishM2 is therefore necessary but not sufficient, since a later sbt session recomputes the version regardless. Committing the retarget makes the tree clean, so dynver stops appending a timestamp and every sbt session in the job computes the same version. The OSS side already demonstrates the end state: its checkout is never edited, so its version carries no timestamp at all (1.1.3-python3.13-102-bfba9c82-SNAPSHOT in that same build). Verified against git directly: clean tree v1.1.3.0-1-g0a826a4f tracked file modified (the sed) v1.1.3.0-1-g0a826a4f+DIRTY after commit v1.1.3.0-2-gdc0c4fca + gitignored target/ + untracked v1.1.3.0-2-gdc0c4fca The last row matters for correctness here: 'git describe --dirty', which is what dynver reads, only considers modifications to tracked files, so sbt's own build output cannot reintroduce the drift later in the job. A diff-index guard fails the step loudly if the tree is ever dirty at that point anyway. The commit is local to the agent's checkout and never pushed. Targeted at spark4.1 rather than master so the fix is validated on a branch where this check actually fails, and against SynapseML-Internal's own spark4.1 branch. Also fixes the diagnostic listing published versions: it hardcoded '*-internal_2.12', so on this branch, which builds _2.13, it printed nothing - exactly where a version mismatch most needed to be visible. Internal Scala tests were never affected: sbt resolves through its own classpath and never goes through Ivy for the Internal artifact. Fixes #2653 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
b231307 to
7188225
Compare
|
/azp run microsoft.SynapseML |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
Addresses review feedback on the guard added by this PR: 'git commit -am' exits non-zero when nothing is staged, and this step runs under 'set -e', so an unconditional commit would turn the 'already retargeted' case into a hard step failure and make the step non-idempotent on retry. Verified against git directly: git commit --quiet --no-verify -am (nothing staged) -> exit 1 git diff-index --quiet HEAD -- (clean tree) -> exit 0 git diff-index --quiet HEAD -- (dirty tree) -> exit 1 The preceding grep -qF checks already prove build.sbt holds the requested synapseMLVersion and Resolver.mavenLocal, so an unchanged tree is a valid state rather than an error: it means the edits were already in place. Either way dynver sees a clean tree and emits one stable version, which is the property the rest of the job depends on. The post-commit diff-index guard is unchanged and still fails loudly if the tree is dirty at that point. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
/azp run microsoft.SynapseML |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
Fixes #2653. Follow-up to #2645, which merged with this check red.
Targeted at
spark4.1rather thanmasterso the fix is validated where the failure actually reproduces — and against SynapseML-Internal's ownspark4.1branch (confirmed to exist, so the job's branch-matching does not silently fall back to Internalmaster).Symptom
Run Internal Python tests (ExcludeAIFunc)fails on essentially every PR. All ~21 tests error at setup, and the only visible cause is:which hides the real error in the JVM that never reported a port:
Root cause
Internal's version comes from sbt-dynver, which appends a live
-<yyyyMMdd>-<HHmm>suffix whenever the working tree is dirty, recomputed from the wall clock on every sbt load. Internal'sbuild.sbtsetsdynverSonatypeSnapshots := trueanddynverSeparator := "-", which is where the-SNAPSHOTsuffix and the-separators come from.Retarget Internal to this buildruns twosed -icommands againstbuild.sbt. That is what makes the tree dirty — for the rest of the job. Every subsequent sbt session then picks a different version.Timeline from build
231488245(step start times from the ADO timeline, versions from its log):sbt packagePython publishM2make_mlflow_models.pyfound … in local-m2-cache) ✅sbt testPythonExcludeAIFuncre-runsCodeGenin a new sessionThe fixtures read the coordinate from the installed package:
Two consequences worth calling out:
make_mlflow_models.pysucceeds — it is not the culprit, contrary to what the existing comment in this file says. It runs before the rebake.Fix
Commit the retarget edits, so the tree is clean and dynver emits one stable version for the whole job.
The OSS side already demonstrates the target end state — its checkout is never edited, so its version carries no timestamp at all:
1.1.3-python3.13-102-bfba9c82-SNAPSHOT, in that very same build.What the version becomes
No timestamp. Internal's version changes shape from
-SNAPSHOTis retained becausedynverSonatypeSnapshots := trueand the distance is non-zero. This is the same shape SynapseML-Internal's own CI produces for its branches, since those checkouts are clean.Verification
The fix rests on two properties of
git describe --dirty(what dynver reads). Both checked against git directly rather than assumed:git describe --long --tags --dirtyv1.1.3.0-1-g0a826a4fsed)v1.1.3.0-1-g0a826a4f**+DIRTY**v1.1.3.0-2-gdc0c4fcatarget/+ untracked filesv1.1.3.0-2-gdc0c4fcaThe last row is the one that matters:
--dirtyconsiders only modifications to tracked files, so sbt's own build output (.gitignorealready excludes**/target/*) cannot reintroduce the drift mid-job. Agit diff-indexguard fails the step loudly if the tree is somehow dirty anyway.The commit is local to the agent's checkout and is never pushed.
Also fixed
The diagnostic listing published versions hardcoded
*-internal_2.12. On this branch, which builds_2.13, it printed nothing — exactly where a version mismatch most needed to be visible.Scope
Internal Scala tests were never affected: sbt resolves through its own classpath and never goes through Ivy for the Internal artifact. That is why they pass in the same builds where the Python step fails.
The job stays
continueOnError: trueand is not a required status check. This restores a signal that has been permanently red; it does not change what gates merges.