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.
chanseokoh
left a comment
There was a problem hiding this comment.
Overall, I'm not very familiar with how exactly this works, so there are many questions.
| } | ||
|
|
||
| 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.
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.
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.
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.
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.
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.
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.
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.
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?
| var watchedFiles = map[string]filesLists{} | ||
|
|
||
| func GetBuildDefinitions(a *latest.JibArtifact) []string { | ||
| return watchedFiles[a.Project].BuildDefinitions |
There was a problem hiding this comment.
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.
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.
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.
| 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.
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.
|| 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.
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.
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.
| // 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