-
Notifications
You must be signed in to change notification settings - Fork 121
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
Support local-to-remote mirroring #717
Conversation
Motivation We currently support only remote-to-local mirroring. We should support the opposite direction as well. Modifications: - Implement `GitMirror#mirrorToLocalRemote()` which threw `UnsupportedOperationException` before. Result: - Close line#53 - You can now enable mirroring from Central Dogma to a remote Git server.
Codecov ReportBase: 64.95% // Head: 65.11% // Increases project coverage by
Additional details and impacted files@@ Coverage Diff @@
## master #717 +/- ##
============================================
+ Coverage 64.95% 65.11% +0.15%
- Complexity 3227 3259 +32
============================================
Files 349 349
Lines 13474 13670 +196
Branches 1454 1485 +31
============================================
+ Hits 8752 8901 +149
- Misses 3896 3928 +32
- Partials 826 841 +15
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. ☔ View full report at Codecov. |
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.
Review in progress. Don't need to address the comments at the moment. I am going to review the remaining things tomorrow. Mostly looking good though. 😉
server/src/main/java/com/linecorp/centraldogma/server/internal/mirror/GitMirror.java
Outdated
Show resolved
Hide resolved
server/src/main/java/com/linecorp/centraldogma/server/internal/mirror/GitMirror.java
Show resolved
Hide resolved
server/src/main/java/com/linecorp/centraldogma/server/internal/mirror/GitMirror.java
Outdated
Show resolved
Hide resolved
server/src/main/java/com/linecorp/centraldogma/server/internal/mirror/GitMirror.java
Outdated
Show resolved
Hide resolved
server/src/main/java/com/linecorp/centraldogma/server/internal/mirror/GitMirror.java
Outdated
Show resolved
Hide resolved
@ikhoon Addressed all the comments. 😉 |
The master branch is merged into the working branch to run in Apple Silicon. |
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.
Left minor comments and questions.
server/src/main/java/com/linecorp/centraldogma/server/internal/mirror/GitMirror.java
Outdated
Show resolved
Hide resolved
server/src/main/java/com/linecorp/centraldogma/server/internal/mirror/GitMirror.java
Show resolved
Hide resolved
server/src/main/java/com/linecorp/centraldogma/server/internal/mirror/GitMirror.java
Show resolved
Hide resolved
server/src/main/java/com/linecorp/centraldogma/server/internal/mirror/GitMirror.java
Outdated
Show resolved
Hide resolved
server/src/main/java/com/linecorp/centraldogma/server/internal/mirror/GitMirror.java
Outdated
Show resolved
Hide resolved
server/src/main/java/com/linecorp/centraldogma/server/internal/mirror/GitMirror.java
Outdated
Show resolved
Hide resolved
server/src/main/java/com/linecorp/centraldogma/server/internal/mirror/GitMirror.java
Outdated
Show resolved
Hide resolved
@Override | ||
public void apply(DirCacheEntry ent) { | ||
try { | ||
ent.setObjectId(inserter.insert(Constants.OBJ_BLOB, text.getBytes(UTF_8))); |
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 we eventually convert text
into bytes, should we directly write JsonNode
into bytes and use it as is? It would allocate fewer objects.
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 thought we need to convert to String
first because we need to use the pretty printer. If we use bytes, the mirror repository would see something like
{"a": {"foo":"bar"}}
instead of
{
"a" : {
"foo": "bar"
}
}
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 see, I think this is reasonable:
- For remote git repositories, we pretty print
- For local storage, we save the compact json
server/src/main/java/com/linecorp/centraldogma/server/internal/mirror/GitMirror.java
Outdated
Show resolved
Hide resolved
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.
@ikhoon All addressed. PTAL. 😄
server/src/main/java/com/linecorp/centraldogma/server/internal/mirror/GitMirror.java
Outdated
Show resolved
Hide resolved
server/src/main/java/com/linecorp/centraldogma/server/internal/mirror/GitMirror.java
Outdated
Show resolved
Hide resolved
@Override | ||
public void apply(DirCacheEntry ent) { | ||
try { | ||
ent.setObjectId(inserter.insert(Constants.OBJ_BLOB, text.getBytes(UTF_8))); |
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 thought we need to convert to String
first because we need to use the pretty printer. If we use bytes, the mirror repository would see something like
{"a": {"foo":"bar"}}
instead of
{
"a" : {
"foo": "bar"
}
}
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.
Sorry about the late review 😅 I think this PR looks pretty much done 👍 Left some minor comments
|
||
git.push() | ||
.setRefSpecs(new RefSpec(headBranchRefName)) | ||
.setForce(true) |
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 think this can potentially overwrite commits. (e.g. multiple local repositories are pushed to a single remote repository, mis-operation can overwrite a repository, etc..)
If there is a conflict, what do you think of just failing the current iteration and allowing to update on the next iteration of mirror
?
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 there is a conflict, what do you think of just failing the current iteration and allowing to update on the next iteration of mirror?
That's a good suggestion. 👍
final Stream<Map.Entry<String, Entry<?>>> entryStream = | ||
localRawHeadEntries.entrySet() | ||
.stream(); | ||
if (ignoreNode == null) { | ||
// Use HashMap to manipulate it. | ||
return entryStream.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); | ||
} |
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.
minor nit; to avoid allocating an extra Stream
object
final Stream<Map.Entry<String, Entry<?>>> entryStream = | |
localRawHeadEntries.entrySet() | |
.stream(); | |
if (ignoreNode == null) { | |
// Use HashMap to manipulate it. | |
return entryStream.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); | |
} | |
if (ignoreNode == null) { | |
// Use HashMap to manipulate it. | |
return localRawHeadEntries; | |
} | |
final Stream<Map.Entry<String, Entry<?>>> entryStream = localRawHeadEntries.entrySet().stream(); |
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.
We cannot just return localRawHeadEntries
because it can be a singleton map that is not modifiable.
We do remove
operation on the returned map.
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 see 😅
throw new UnsupportedOperationException(); | ||
try (Git git = openGit(workDir)) { | ||
final String headBranchRefName = Constants.R_HEADS + remoteBranch(); | ||
final ObjectId headCommitId = fetchRemoteHeadAndGetCommitId(git, headBranchRefName); |
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) Just curious, what happens if the branch doesn't exist in the remote repository? Will an exception be thrown? (as opposed to creating a branch if it doesn't exist)
I think it's fine to throw an exception, just want to make sure the behavior is known/defined.
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 actually checked and verified an exception is thrown 👍
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.
Thanks for checking it. 😉
// Use InsertText to store the content in pretty format | ||
final String newContent = newJsonNode.toPrettyString() + '\n'; | ||
applyPathEdit(dirCache, new InsertText(pathString, inserter, newContent)); | ||
return newContent.length(); |
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; technically String.length()
isn't really the number of bytes (although I guess this is a more lenient upper-bound so this discrepancy isn't very critical)
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.
Yeah, because it's not critical let's leave it as is.
Will get back and use
reader.getObjectSize(inserted, ObjectReader.OBJ_ANY)
if it becomes a problem. 😉
/** | ||
* Removes {@code \r} and appends {@code \n} on the last line if it does not end with {@code \n}. | ||
*/ | ||
private static String sanitizeText(String text) { |
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.
Q) Is it possible to use a common method for this logic? (with storage side)
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.
It seems like this comment wasn't addressed. Is it possible to dedup this method?
Line 1166 in 0cf990c
private static String sanitizeText(String text) { |
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.
Ah forgot to leave a comment about this. 😄
I didn't intentionally dedup logic because of this PR #681
Let me cleanup the logic in the PR! 😉
@Override | ||
public void apply(DirCacheEntry ent) { | ||
try { | ||
ent.setObjectId(inserter.insert(Constants.OBJ_BLOB, text.getBytes(UTF_8))); |
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 see, I think this is reasonable:
- For remote git repositories, we pretty print
- For local storage, we save the compact json
" \"direction\": \"" + direction + "\"," + | ||
" \"localRepo\": \"" + localRepo + "\"," + | ||
(localPath != null ? "\"localPath\": \"" + localPath + "\"," : "") + | ||
" \"remoteUri\": \"" + gitUri + firstNonNull(remotePath, "") + '"' + |
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.
What do you think of also adding this to GitMirrorTest
as well?
" \"remoteUri\": \"" + gitUri + firstNonNull(remotePath, "") + '"' + | |
" \"remoteUri\": \"" + gitUri + firstNonNull(remotePath, "") + '"' + | |
" \"schedule\": \"0 0 0 1 1 ? 2099\"," + |
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.
Thanks! Added. 😉
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.
Left a minor comment but looks good to me 👍 Thanks @minwoox 👍 🙇 🚀 !
final Stream<Map.Entry<String, Entry<?>>> entryStream = | ||
localRawHeadEntries.entrySet() | ||
.stream(); | ||
if (ignoreNode == null) { | ||
// Use HashMap to manipulate it. | ||
return entryStream.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); | ||
} |
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 see 😅
/** | ||
* Removes {@code \r} and appends {@code \n} on the last line if it does not end with {@code \n}. | ||
*/ | ||
private static String sanitizeText(String text) { |
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.
It seems like this comment wasn't addressed. Is it possible to dedup this method?
Line 1166 in 0cf990c
private static String sanitizeText(String text) { |
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.
Awesome!!! 🚀💯
Motivation
We currently support only remote-to-local mirroring. We should support the opposite direction as well.
Modifications:
GitMirror#mirrorToLocalRemote()
which used to throwUnsupportedOperationException
before.Result: