-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Jib Auto Sync - core + maven implementation #3369
Conversation
So most of this is baked into the jib package, but I don't think anything in it necessarily HAS to be jib specific. If we want to extend the model out for other systems we can. But it mostly feels like a java problem anyway? Gradle is not covered here.
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.
Overall, I'm not very familiar with how exactly this works, so there are many questions.
@@ -85,8 +85,24 @@ func getCommandMaven(ctx context.Context, workspace string, a *latest.JibArtifac | |||
return MavenCommand.CreateCommand(ctx, workspace, args) | |||
} | |||
|
|||
func getSyncMapCommandMaven(ctx context.Context, workspace string, a *latest.JibArtifact) *exec.Cmd { | |||
cmd := MavenCommand.CreateCommand(ctx, workspace, mavenBuildArgs("_skaffold-sync-map", a, 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.
mavenBuildArgs
append either package
or prepare-package
. Does that make sense? And I don't really think _skaffold-sync-map
will work. It should be jib:_skaffold-sync-map
?
Also we pass --quiet
for _skaffold-files_v2
. But I'm not sure if --quiet
helps, because I didn't see any Maven output in your short demo.
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, forget prepending jib:_
. I see it's added in mavenBuildArgs
.
} | ||
|
||
// To parse the output, search for "BEGIN JIB JSON", then unmarshal the next line into the pathMap struct. | ||
matches := regexp.MustCompile(`BEGIN JIB JSON\r?\n({.*})`).FindSubmatch(stdout) |
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 liked @briandealwis's idea to have this extensible. Something we should keep in mind to make things match.
if (err != nil) { | ||
return err | ||
} | ||
syncLists[artifact.Project] = *syncMap |
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.
Is it possible that multiple Java projects have the same "JibArtifact.Project" name as a key to the map? The map is a global map, isn't it?
And isn't artifact.Project == ""
for a single-module project setup?
Src string `json:"src"` | ||
Dest string `json:"dest"` | ||
|
||
filetime time.Time |
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.
Does an entry always refer to a file and not a directory? Just to understand what the modification time of a directory may mean, if it can be a directory. But I kind of remember that our sync map will list every single file and not directories?
And I always forget. For a Java file, will the entry be a .java
file or a .class
file?
} | ||
for _, bf := range buildFiles { | ||
if f == bf { | ||
return nil, nil, nil |
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.
Considering the method name, I think it's worth adding a comment nil, nil
means a full rebuild.
} | ||
|
||
// we need to do another build and get a new sync map | ||
newSyncMap, err := getSyncMap(ctx, workspace, artifact) |
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.
Oh, so getSyncMap
is really supposed to trigger a build itself (i.e., doing package
)?
I'd like to understand how this could potentially interfere (race condition) with normal file watching, as we talked about before.
func updateModTime(se []SyncEntry) error { | ||
for i, _ := range se { | ||
e := &se[i] | ||
if info, err := os.Stat(e.Src); err != nil { |
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 hope this (and the operation to compute absolute path above) is cheap to do. The sync map will inherently be small, so we should probably good?
@@ -88,6 +88,10 @@ type filesLists struct { | |||
// watchedFiles maps from project name to watched files | |||
var watchedFiles = map[string]filesLists{} | |||
|
|||
func GetBuildDefinitions(a *latest.JibArtifact) []string { | |||
return watchedFiles[a.Project].BuildDefinitions |
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 suspect a.Project
needs to be combined with the artifact context
. As @chanseokoh noted below, a.Project
may be the empty string, and it's entirely possible to have two artifacts with different contexts (workspaces) and same project name.
|
||
var syncLists = make(map[string]SyncMap) | ||
|
||
type SyncMap struct { |
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.
Add some go doc to describe that this structure is filled in from information from the jib builder (mvn jib:_...
/gradle _jib...
). I have to admit that Direct
didn't have obvious semantics, but I don't have anything better to suggest.
// returns toCopy, toDelete, error | ||
func GetSyncDiff(ctx context.Context, workspace string, artifact *latest.JibArtifact, e filemon.Events) (map[string][]string, map[string][]string, error) { |
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.
You could consider using named return values to make this more explicit:
func GetSyncDiff(...) (toCopy map[string][]string, toDelete map[string][]string, err error) {
Or maybe it'd be better to return an explicit struct SyncDiff
?
|
||
if len(e.Deleted) != 0 { | ||
// change into logging | ||
fmt.Println("Deletions are not supported by jib auto sync at the moment") |
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.
fmt.Println("Deletions are not supported by jib auto sync at the moment") | |
fmt.Println("Deletions are not yet supported by jib auto sync") |
return errors.New("failed to get Jib Sync data") | ||
} | ||
|
||
line := bytes.Replace(matches[1], []byte(`\`), []byte(`\\`), -1) |
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 line throws me every time I come across it. I believe this replace is required is because of Windows paths? Should we instead fix our emitting code in Jib?
|
||
|
||
// if all files are modified and direct, we don't need to build anything | ||
if len(e.Deleted) == 0 || len(e.Added) == 0 { |
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.
||
seems at odd with the comment? And we could skip this if len(e.Modified) > len(oldSyncMap.Direct)
?
if !filepath.IsAbs(f) { | ||
if ff, err := filepath.Abs(f); err != nil{ | ||
return nil, nil, err | ||
} else { | ||
f = ff | ||
} | ||
} |
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 could be moved to the outer for
loop
} | ||
|
||
// it seems less than efficient to keep the original JSON structure when doing look ups, so maybe we should only use the json objects for serialization | ||
// and store the sync data in a better type? |
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.
The original format leads to O(n) checks
if a.Sync != nil && a.Sync.Auto != nil{ | ||
if a.JibArtifact != nil { | ||
if err := jib2.InitSync(ctx, a.Workspace, a.JibArtifact); err != nil { | ||
// maybe should just disable sync here, instead of dead? |
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.
// maybe should just disable sync here, instead of dead? | |
// maybe should just disable sync here, instead of dying? |
this is covered by a bunch of new PRs |
Just putting this up here for now, if it feels like I'm messing too much with the core system maybe we can restructure that. I'll clean up the code some more and add tests if we can kinda agree on the changes in here?
So most of this is baked into the jib package, but I don't think anything in it necessarily HAS to be jib specific. If we want to extend the model out for other systems we can. But it mostly
feels like a java problem anyway?
Added an InitSync before first build in dev mode to initalize sync of the files - requires a build to get the sync data
I feel like there's some weird cases I haven't handled yet
logging is wrong, probably need to fix that