-
Notifications
You must be signed in to change notification settings - Fork 130
Run a mirror when its configuration is updated #1247
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Motivation: See line#1183 for details. Modifications: - Add `remoteRevision`, `remotePath`, `localRevision` and `localPath` fields to `.mirror_state.json` to capture the configuration used when a mirror is run. - Move `RepositoryUri` to public API and expose it in `Mirror.java` to allow access to the full remote URL in mirror tasks. - Set the depth of Git fetch to 2 to read parent commit. - This is required to detect changes made on the remote in local-to-remote mode. Result: - Git mirroring now runs correctly when the configuration changes or the mirrored content is manually overridden. - Closes line#1183
📝 WalkthroughWalkthroughRefactors mirror remote tracking from separate URI/path/branch fields into a unified Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Mirror System
participant Processor as AbstractGitMirror
participant LocalRepo as Local Repository
participant RemoteRepo as Remote Repository
Client->>Processor: mirrorLocalToRemote()
activate Processor
Processor->>LocalRepo: read head commit & parent
Processor->>RemoteRepo: fetch head commit (depth=2)
Processor->>Processor: localCurrentMirrorState(), remoteCurrentMirrorState()
Processor->>Processor: shouldRunLocalToRemote(oldState, localHead, remoteCommitId)
alt Decision: RUN / COMPARE_AND_RUN
Processor->>RemoteRepo: push changes for configured remotePath(s)
Processor->>LocalRepo: write updated mirror_state.json (per-path, configHash)
Processor-->>Client: mirror completed
else Decision: SKIP
Processor-->>Client: skip — up-to-date
end
deactivate Processor
Client->>Processor: mirrorRemoteToLocal()
activate Processor
Processor->>RemoteRepo: fetch head commit (depth=2)
Processor->>Processor: remoteCurrentMirrorState()
Processor->>LocalRepo: read local mirror_state.json
Processor->>Processor: shouldRunRemoteToLocal(oldState, localHead, remoteCommitId)
alt Decision: RUN / COMPARE_AND_RUN
Processor->>LocalRepo: apply remote changes to per-path localPath(s)
Processor->>LocalRepo: write updated mirror_state.json (per-path, configHash)
Processor-->>Client: mirror completed
else Decision: SKIP
Processor-->>Client: skip — up-to-date
end
deactivate Processor
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
jrhee17
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 👍
| * | ||
| * @deprecated Use {@link #remoteRevision()} or {@link #localRevision()} instead. | ||
| */ | ||
| @Deprecated |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit; the proposed logic always gets the previous revision of the target (local for remote2local, remote for local2remote).
It might be easier to understand if a separate previousTargetRevision() method is added.
| } | ||
| } | ||
|
|
||
| boolean shouldRunMirror(@Nullable MirrorState oldMirrorState, Revision localHead, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question) If there are multiple mirrors configured for a single repository, even if a file in directory A is changed will all mirrors be triggered due to also looking at the target revision?
I wonder if adding a hash of the mirror configuration for each mirror to MirrorState is a simpler/safer approach
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an important point. I hadn't considered that multiple mirrors can be configured for a single repository.
if adding a hash of the mirror configuration for each mirror to MirrorState is a simpler/safer approach
A mirror configuration hash alone can't tell us whether we should run the mirror when there has been a manual push to the local repo. However, it would be worth adding the hash to detect any changes in mirror configuration.
Although, the logic is complex, the following approach seems to be the most appropriate under the Central Dogma revision model which does not provide the HEAD revision for a directory or a file.
Proposal: For remote-to-local mirroring, it should be safe to rely on the local HEAD when the local path is the root (/). If the local path is non-root (e.g, /subdir) and previousLocalRevision + 1 doesn't match the current HEAD, we could run repository.diff(previousLocalRevision, headRevision, "/subdir") to check whether there are actual changes happened before running a mirror task.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if adding a hash of the mirror configuration for each mirror to MirrorState is a simpler/safer approach
I interpreted the original issue as mirror doesn't run properly when the path (MirrorState) changes. I didn't think it was necessary that the revision of the target is considered for mirroring.
Whatever approach is taken, I'm fine as long as it's consistent for both directions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made new changes to properly overwrite a manual push in the next mirror job. PTAL.
jrhee17
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 👍
| return MirrorDecision.RUN; | ||
| } | ||
|
|
||
| if (!localHead.text().equals(previousLocalRevision)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question) I may have not understood as the logic is complex, but is localHead ever equal to previousLocalRevision given that
localHeadis the currentHEADrevisionpreviousLocalRevisionis from theMirrorState, which means it is the previous local revision?
| @JsonCreator | ||
| MirrorState(@JsonProperty(value = "sourceRevision", required = true) String sourceRevision) { | ||
| public MirrorState(@JsonProperty(value = "sourceRevision", required = true) String sourceRevision, | ||
| @JsonProperty("remoteRevision") @Nullable String remoteRevision, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understood that:
- For
localToRemoteremoteRevisionis the revision before the mirror push (so the previous version)localRevisionis the previous revision before mirror push
- For
remoteToLocalremoteRevisionis the current revision (since no push is done to remote)localRevisionis the previous revision before mirror push
It might be worth documenting this somewhere since I don't think this is well expressed in the currently proposed code.
| } | ||
|
|
||
| private MirrorDecision shouldRunLocalToRemote(@Nullable MirrorState oldMirrorState, Revision localHead, | ||
| @Nullable ObjectId remoteCommitId) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optional) Naming if a commit id is previous or not may help understand the condition more clearly. Ditto for shouldRunRemoteToLocal
| @Nullable ObjectId remoteCommitId) | |
| @Nullable ObjectId prevRemoteCommitId) |
where the previous prevRemoteCommitId could be mirror*
Motivation:
See #1183 for details.
Modifications:
remoteRevision,remotePath,localRevisionandlocalPathfields to.mirror_state.jsonto capture the configuration used when a mirror is run.RepositoryUrito public API and expose it inMirror.javato allow access to the full remote URL in mirror tasks.Result: