55 "context"
66 "fmt"
77 "log/slog"
8+ "net/url"
89 "os"
910 "os/exec"
1011 "path/filepath"
@@ -1496,9 +1497,14 @@ func (e *TestExecutor) executeGitClone(block api.ParsedComponent, step TestStep,
14961497 // Store outputs
14971498 result .Outputs ["clone_path" ] = absolutePath
14981499 result .Outputs ["file_count" ] = fmt .Sprintf ("%d" , fileCount )
1500+ result .Outputs ["repo_url" ] = cloneURL
14991501 if ref != "" {
15001502 result .Outputs ["ref" ] = ref
15011503 }
1504+ if owner , repo := parseOwnerRepoFromURL (cloneURL ); owner != "" {
1505+ result .Outputs ["repo_owner" ] = owner
1506+ result .Outputs ["repo_name" ] = repo
1507+ }
15021508
15031509 // Store in block outputs for downstream access
15041510 e .blockOutputs [block .ID ] = result .Outputs
@@ -1519,7 +1525,34 @@ func (e *TestExecutor) executeGitClone(block api.ParsedComponent, step TestStep,
15191525 return result
15201526}
15211527
1528+ // parseOwnerRepoFromURL extracts owner/repo from HTTPS or SSH-style git URLs.
1529+ func parseOwnerRepoFromURL (rawURL string ) (owner , repo string ) {
1530+ // Handle SSH-style URLs: git@github.com:org/repo.git
1531+ if idx := strings .Index (rawURL , ":" ); idx >= 0 && ! strings .Contains (rawURL , "://" ) {
1532+ path := rawURL [idx + 1 :]
1533+ path = strings .TrimSuffix (path , ".git" )
1534+ parts := strings .SplitN (path , "/" , 3 )
1535+ if len (parts ) >= 2 {
1536+ return parts [0 ], parts [1 ]
1537+ }
1538+ return "" , ""
1539+ }
1540+
1541+ parsed , err := url .Parse (rawURL )
1542+ if err != nil {
1543+ return "" , ""
1544+ }
1545+
1546+ path := strings .Trim (parsed .Path , "/" )
1547+ path = strings .TrimSuffix (path , ".git" )
15221548
1549+ parts := strings .SplitN (path , "/" , 3 )
1550+ if len (parts ) < 2 {
1551+ return "" , ""
1552+ }
1553+
1554+ return parts [0 ], parts [1 ]
1555+ }
15231556
15241557// getSessionEnvVar reads an env var from the session.
15251558func (e * TestExecutor ) getSessionEnvVar (key string ) string {
0 commit comments