-
Notifications
You must be signed in to change notification settings - Fork 0
Hybrid polyglot testkit: Go CLI bridge + wrapper smoke tests #6
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
Merged
Merged
Changes from all commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
8915b71
Add hybrid roadmap and Go CLI bridge foundation
cursoragent 36109dd
Fix wrapper smoke test edge cases and Java build config
cursoragent 776d38b
Add reverse spec-kit scaffold and conformance checks
cursoragent 223fc2b
Normalize to canonical spec-kit enforcement and CI gates
cursoragent 199142a
Fix CLI stream deadlock and stdout-only git queries
cursoragent c2cf9b0
Address PR review findings and harden bridge behavior
cursoragent 8c1a543
Fix CLI bridge JSON parsing and branch helper consistency
cursoragent b70b94c
Fix JSON escaping and always emit snapshotSize
cursoragent 0dd9a6c
Resolve remaining PR feedback on CLI JSON responses
cursoragent 59b16b4
Add runnable Python/Java smoke samples and CI validation
cursoragent 65dcb89
Resolve PR feedback: path safety and portability
cursoragent ca494d5
Make Java parser tests use in-memory CLI stub
cursoragent bb70df4
Normalize Java sample workspace root path
cursoragent 9763225
Enhance Java wrapper API with typed repo options
cursoragent 3af8349
Harden bridge portability and expand CI OS coverage
cursoragent 58969a6
Resolve latest PR feedback on bridge robustness
cursoragent aee8e65
Harden Java sample cleanup for Windows file locks
cursoragent 112878b
Make Java sample cleanup tolerant to Windows locks
cursoragent 054f0b3
Use TempDir in Java sample to avoid temp leaks
cursoragent 087e484
Restore symlink entries in snapshot roundtrips
cursoragent b87d1d8
Add gofmt check to CI test job
cursoragent 366c69e
Fix snapshot archive/restore entry symmetry
cursoragent 372011c
Optimize CI wrapper tests with prebuilt CLI
cursoragent 33df51f
Fix wrapper CI CLI path after directory changes
cursoragent 347c4e8
Fix wrapper CLI path resolution across CI jobs
cursoragent 6cc6ae3
fix: apply CodeRabbit auto-fixes
coderabbitai[bot] 21de822
Fix CodeRabbit Java lambda compile regression
cursoragent File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,266 @@ | ||
| package main | ||
|
|
||
| import ( | ||
| "encoding/json" | ||
| "fmt" | ||
| "os" | ||
| "path/filepath" | ||
| "strings" | ||
|
|
||
| testutil "github.com/git-fire/git-testkit" | ||
| ) | ||
|
|
||
| type request struct { | ||
| Op string `json:"op"` | ||
| BaseDir string `json:"baseDir,omitempty"` | ||
| RepoPath string `json:"repoPath,omitempty"` | ||
| Args []string `json:"args,omitempty"` | ||
| Options *repoOptionsInput `json:"options,omitempty"` | ||
|
|
||
| SnapshotPath string `json:"snapshotPath,omitempty"` | ||
| } | ||
|
|
||
| type repoOptionsInput struct { | ||
| Name string `json:"name"` | ||
| Dirty bool `json:"dirty,omitempty"` | ||
| Files map[string]string `json:"files,omitempty"` | ||
| Remotes map[string]string `json:"remotes,omitempty"` | ||
| Branches []string `json:"branches,omitempty"` | ||
| InitialCommit string `json:"initialCommit,omitempty"` | ||
| } | ||
|
|
||
| type response struct { | ||
| OK bool `json:"ok"` | ||
|
|
||
| Error string `json:"error,omitempty"` | ||
|
|
||
| RepoPath string `json:"repoPath,omitempty"` | ||
| RemotePath string `json:"remotePath,omitempty"` | ||
| FSRoot string `json:"fsRoot,omitempty"` | ||
| Output *string `json:"output,omitempty"` | ||
| Dirty *bool `json:"dirty,omitempty"` | ||
| Remotes map[string]string `json:"remotes,omitempty"` | ||
| SHA string `json:"sha,omitempty"` | ||
| Branches []string `json:"branches,omitempty"` | ||
| SnapshotName string `json:"snapshotName,omitempty"` | ||
| SnapshotSize *int `json:"snapshotSize,omitempty"` | ||
| RestorePath string `json:"restorePath,omitempty"` | ||
| } | ||
|
|
||
| func main() { | ||
| req, err := parseRequest() | ||
| if err != nil { | ||
| writeResponse(response{OK: false, Error: err.Error()}) | ||
| os.Exit(1) | ||
| } | ||
|
|
||
| res, err := handle(req) | ||
| if err != nil { | ||
| writeResponse(response{OK: false, Error: err.Error()}) | ||
| os.Exit(1) | ||
| } | ||
| writeResponse(res) | ||
| } | ||
|
|
||
| func parseRequest() (request, error) { | ||
| var req request | ||
| if err := json.NewDecoder(os.Stdin).Decode(&req); err != nil { | ||
| return request{}, fmt.Errorf("invalid JSON request: %w", err) | ||
| } | ||
| if strings.TrimSpace(req.Op) == "" { | ||
| return request{}, fmt.Errorf("missing required field: op") | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| return req, nil | ||
| } | ||
|
|
||
| func handle(req request) (response, error) { | ||
| switch req.Op { | ||
| case "create_test_repo": | ||
| base, err := ensureBaseDir(req.BaseDir) | ||
| if err != nil { | ||
| return response{}, err | ||
| } | ||
| if req.Options == nil { | ||
| return response{}, fmt.Errorf("missing options") | ||
| } | ||
| repoPath, err := testutil.CreateTestRepoInDir(base, testutil.RepoOptions{ | ||
| Name: req.Options.Name, | ||
| Dirty: req.Options.Dirty, | ||
| Files: req.Options.Files, | ||
| Remotes: req.Options.Remotes, | ||
| Branches: req.Options.Branches, | ||
| InitialCommit: req.Options.InitialCommit, | ||
| }) | ||
| if err != nil { | ||
| return response{}, err | ||
| } | ||
| return response{OK: true, RepoPath: repoPath}, nil | ||
|
|
||
| case "create_bare_remote": | ||
| base, err := ensureBaseDir(req.BaseDir) | ||
| if err != nil { | ||
| return response{}, err | ||
| } | ||
| if req.Options == nil || req.Options.Name == "" { | ||
| return response{}, fmt.Errorf("missing options.name") | ||
| } | ||
| remotePath, err := testutil.CreateBareRemoteInDir(base, req.Options.Name) | ||
| if err != nil { | ||
| return response{}, err | ||
| } | ||
| return response{OK: true, RemotePath: remotePath}, nil | ||
|
|
||
| case "setup_fake_filesystem": | ||
| base, err := ensureBaseDir(req.BaseDir) | ||
| if err != nil { | ||
| return response{}, err | ||
| } | ||
| root, err := testutil.SetupFakeFilesystemInDir(base) | ||
| if err != nil { | ||
| return response{}, err | ||
| } | ||
| return response{OK: true, FSRoot: root}, nil | ||
|
|
||
| case "run_git_cmd": | ||
| if req.RepoPath == "" { | ||
| return response{}, fmt.Errorf("missing repoPath") | ||
| } | ||
| output, err := testutil.RunGitCmdE(req.RepoPath, req.Args...) | ||
| if err != nil { | ||
| return response{}, err | ||
| } | ||
| return response{OK: true, Output: stringPtr(output)}, nil | ||
|
|
||
| case "is_dirty": | ||
| if req.RepoPath == "" { | ||
| return response{}, fmt.Errorf("missing repoPath") | ||
| } | ||
| dirty, err := testutil.IsDirtyE(req.RepoPath) | ||
| if err != nil { | ||
| return response{}, err | ||
| } | ||
| return response{OK: true, Dirty: &dirty}, nil | ||
|
|
||
| case "get_remotes": | ||
| if req.RepoPath == "" { | ||
| return response{}, fmt.Errorf("missing repoPath") | ||
| } | ||
| remotes, err := testutil.GetRemotesE(req.RepoPath) | ||
| if err != nil { | ||
| return response{}, err | ||
| } | ||
| return response{OK: true, Remotes: remotes}, nil | ||
|
|
||
| case "get_current_sha": | ||
| if req.RepoPath == "" { | ||
| return response{}, fmt.Errorf("missing repoPath") | ||
| } | ||
| sha, err := testutil.GetCurrentSHAE(req.RepoPath) | ||
| if err != nil { | ||
| return response{}, err | ||
| } | ||
| return response{OK: true, SHA: sha}, nil | ||
|
|
||
| case "get_branches": | ||
| if req.RepoPath == "" { | ||
| return response{}, fmt.Errorf("missing repoPath") | ||
| } | ||
| branches, err := testutil.GetBranchesE(req.RepoPath) | ||
| if err != nil { | ||
| return response{}, err | ||
| } | ||
| return response{OK: true, Branches: branches}, nil | ||
|
|
||
| case "snapshot_repo": | ||
| if req.RepoPath == "" { | ||
| return response{}, fmt.Errorf("missing repoPath") | ||
| } | ||
| snapshot, err := testutil.SnapshotRepoE(req.RepoPath) | ||
| if err != nil { | ||
| return response{}, err | ||
| } | ||
| return response{ | ||
| OK: true, | ||
| SnapshotName: snapshot.Name(), | ||
| SnapshotSize: intPtr(snapshot.Size()), | ||
| }, nil | ||
|
|
||
| case "snapshot_save": | ||
| if req.RepoPath == "" || req.SnapshotPath == "" { | ||
| return response{}, fmt.Errorf("missing repoPath or snapshotPath") | ||
| } | ||
| snapshot, err := testutil.SnapshotRepoE(req.RepoPath) | ||
| if err != nil { | ||
| return response{}, err | ||
| } | ||
| if err := testutil.SaveSnapshotToDiskE(snapshot, req.SnapshotPath); err != nil { | ||
| return response{}, err | ||
| } | ||
| return response{ | ||
| OK: true, | ||
| SnapshotName: snapshot.Name(), | ||
| SnapshotSize: intPtr(snapshot.Size()), | ||
| }, nil | ||
|
|
||
| case "snapshot_load_restore": | ||
| if req.SnapshotPath == "" { | ||
| return response{}, fmt.Errorf("missing snapshotPath") | ||
| } | ||
| base, err := ensureBaseDir(req.BaseDir) | ||
| if err != nil { | ||
| return response{}, err | ||
| } | ||
| snapshot, err := testutil.LoadSnapshotFromDiskE(req.SnapshotPath) | ||
| if err != nil { | ||
| return response{}, err | ||
| } | ||
| restorePath, err := testutil.RestoreSnapshotToDir(snapshot, base) | ||
| if err != nil { | ||
| return response{}, err | ||
| } | ||
| return response{ | ||
| OK: true, | ||
| RestorePath: restorePath, | ||
| SnapshotName: snapshot.Name(), | ||
| SnapshotSize: intPtr(snapshot.Size()), | ||
| }, nil | ||
|
|
||
| default: | ||
| return response{}, fmt.Errorf("unsupported op: %s", req.Op) | ||
| } | ||
| } | ||
|
|
||
| func ensureBaseDir(baseDir string) (string, error) { | ||
| if strings.TrimSpace(baseDir) == "" { | ||
| return "", fmt.Errorf("missing baseDir") | ||
| } | ||
| clean := filepath.Clean(baseDir) | ||
| if err := os.MkdirAll(clean, 0755); err != nil { | ||
| return "", err | ||
| } | ||
| return clean, nil | ||
| } | ||
|
|
||
| func writeResponse(res response) { | ||
| enc := json.NewEncoder(os.Stdout) | ||
| enc.SetEscapeHTML(false) | ||
| if err := enc.Encode(res); err != nil { | ||
| fallback := response{ | ||
| OK: false, | ||
| Error: fmt.Sprintf("failed writing response: %s", err.Error()), | ||
| } | ||
| stderrEnc := json.NewEncoder(os.Stderr) | ||
| stderrEnc.SetEscapeHTML(false) | ||
| if encodeErr := stderrEnc.Encode(fallback); encodeErr != nil { | ||
| fmt.Fprintf(os.Stderr, "failed writing fallback response: %v\n", encodeErr) | ||
| } | ||
| } | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| func intPtr(v int) *int { | ||
| return &v | ||
| } | ||
|
|
||
| func stringPtr(v string) *string { | ||
| return &v | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.