{{ message }}
Repository navigation
fix: strip the token already embedded in remote.origin.url before Repository.push - #1815
Open
MincongZhou wants to merge 2 commits into
Open
MincongZhou wants to merge 2 commits into
MincongZhou wants to merge 2 commits into
Conversation
…ository.push
Repository.push hands the URL read back from the checkout straight to git push:
url = self.git_wrapper.get_repo_remote_url(self.model_dir) # :141
...
self.git_wrapper.push(..., git_token=self.git_token, url=url)
A URL taken from an existing checkout still carries the token that was used at
clone time. config_git_token returns early when the remote already contains
'//oauth2', and _inject_token likewise passes an already-tokened URL through,
so the freshly supplied git_token is silently dropped and git authenticates
with the stale one.
Its twin does not have this problem -- DatasetRepository.push strips first:
remote_url = self.git_wrapper.remove_token_from_url(remote_url) # :255
as does _clone_if_needed at :46. Repository.push is the only one of the three
call sites that omits it, so after a token rotation a push from an existing
checkout fails authentication even though the caller passed a valid token, and
the stale secret stays in .git/config. save_pretrained(push_to_hub=True),
cli/modelcard.py and push_to_hub.py all route through here.
Strip the token for Repository.push as well, leaving git_token as the only
credential used.
Repository.push and DatasetRepository.push both have to hand the git wrapper a URL with no token embedded in it: the URL read back from an existing checkout still carries the clone-time token, and config_git_token/_inject_token leave an already-tokened URL untouched, so the embedded token silently beats the git_token the caller just supplied. The mock git wrapper returns a tokened URL, which is what an existing checkout provides, and the tests assert the URL that reaches git push - for Repository, for DatasetRepository, and that the two agree. They also require the strip to go through remove_token_from_url rather than string surgery at the call site. No checkout and no network access needed.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

What this fixes
Repository.push()hands the URL it reads back from the checkout straight togit push:A URL obtained from an existing checkout still contains the token that was embedded when the repository was cloned.
config_git_tokenreturns early when the remote already contains//oauth2, and_inject_tokenlikewise passes an already-tokened URL through unchanged — soself.git_tokenis silently discarded and git authenticates with the stale token.Its twin does not have this problem.
DatasetRepository.push()strips first, and so does_clone_if_needed:Repository.push()is the only one of the three call sites that omits it.Consequence
After a token rotation, a push from an existing checkout fails authentication even though the caller passed a valid
git_token, and there is no SDK-level recovery — the stale secret stays in.git/config.DatasetRepository.push()with the same token on the same machine works, which makes it look like a permissions problem rather than a stale-credential one.save_pretrained(push_to_hub=True)(patcher.py:487),cli/modelcard.py:141andpush_to_hub.py:66all route throughRepository.push().Reproduction
Both
pushmethods were executed from the real source (function AST extracted and bound to a stub object, with a recordergit_wrapper; only the network-facing wrapper is stubbed). The remote URL the wrapper returns is tokened, exactly as an existing clone would be, and both callers passgit_token="NEW_TOKEN". The recorder prints the URL that would reachgit push:DatasetRepositoryis unchanged in both runs; onlyRepositorychanges.Change
One line:
url = self.git_wrapper.get_repo_remote_url(self.model_dir) + url = self.git_wrapper.remove_token_from_url(url)Validation
python -m py_compile modelscope/hub/repository.py→ OKflake8 --max-line-length=120 modelscope/hub/repository.py→ exit 0yapf --diff(the hook the repo pins at v0.30.0) → no diffNot verified: a real push against the Hub — I have no credentials for it. The claim is limited to what the method passes to the git wrapper, which is where the divergence is. I also did not run the repository's own test suite (it is not installed in this environment).
Notes for reviewers
Repository.push()carries aDeprecationWarningpointing atHubApi().upload_folder. I fixed it rather than leaving it, because the documented successor does not automatically cover the callers that still route through it (save_pretrained(push_to_hub=True),cli/modelcard.py,push_to_hub.py) and because the fix is what makes it agree with its twin. Happy to close this in favour of a different approach if you would rather deprecate harder.Competition
No competing item.
ghsearches forremove_token_from_url,Repository.pushandgit_tokenreturn no matching issue or PR, and none of the six currently open PRs touchesmodelscope/hub/repository.py(the merged #1592 touched onlyhub/api.py).