parseRepoSpec parses repository specification like "org/repo@version" or "org/repo@branch" or "org/repo@commit" Also supports GitHub URLs like "https://github.com/owner/repo[@version]"
(repoSpec string)
| 107 | // parseRepoSpec parses repository specification like "org/repo@version" or "org/repo@branch" or "org/repo@commit" |
| 108 | // Also supports GitHub URLs like "https://github.com/owner/repo[@version]" |
| 109 | func parseRepoSpec(repoSpec string) (*RepoSpec, error) { |
| 110 | specLog.Printf("Parsing repo spec: %q", repoSpec) |
| 111 | parts := strings.SplitN(repoSpec, "@", 2) |
| 112 | repo := parts[0] |
| 113 | var version string |
| 114 | if len(parts) == 2 { |
| 115 | version = parts[1] |
| 116 | specLog.Printf("Version specified: %s", version) |
| 117 | } |
| 118 | |
| 119 | githubHost := getGitHubHost() |
| 120 | githubHostPrefix := githubHost + "/" |
| 121 | githubHostHTTPPrefix := "http://" + strings.TrimPrefix(githubHost, "https://") + "/" |
| 122 | |
| 123 | // Check if this is a GitHub URL |
| 124 | if strings.HasPrefix(repo, githubHostPrefix) || strings.HasPrefix(repo, githubHostHTTPPrefix) { |
| 125 | specLog.Print("Detected GitHub URL format") |
| 126 | // Parse GitHub URL: https://github.com/owner/repo or https://enterprise.github.com/owner/repo |
| 127 | repoURL, err := url.Parse(repo) |
| 128 | if err != nil { |
| 129 | specLog.Printf("Failed to parse GitHub URL: %v", err) |
| 130 | return nil, fmt.Errorf("invalid GitHub URL: %w", err) |
| 131 | } |
| 132 | |
| 133 | // Extract owner/repo from path |
| 134 | pathParts := strings.Split(strings.Trim(repoURL.Path, "/"), "/") |
| 135 | if len(pathParts) != 2 || pathParts[0] == "" || pathParts[1] == "" { |
| 136 | specLog.Printf("Invalid GitHub URL path parts: %v", pathParts) |
| 137 | return nil, fmt.Errorf("invalid GitHub URL: must be %s/owner/repo. Example: %s/github/gh-aw", githubHost, githubHost) |
| 138 | } |
| 139 | |
| 140 | repo = fmt.Sprintf("%s/%s", pathParts[0], pathParts[1]) |
| 141 | specLog.Printf("Extracted repo from URL: %s", repo) |
| 142 | } else if repo == "." { |
| 143 | specLog.Print("Resolving current directory as repo") |
| 144 | // Handle current directory as repo (local workflow) |
| 145 | currentRepo, err := GetCurrentRepoSlug() |
| 146 | if err != nil { |
| 147 | specLog.Printf("Failed to get current repo: %v", err) |
| 148 | return nil, fmt.Errorf("failed to get current repository info: %w", err) |
| 149 | } |
| 150 | repo = currentRepo |
| 151 | specLog.Printf("Resolved current repo: %s", repo) |
| 152 | } else { |
| 153 | // Validate repository format (org/repo) |
| 154 | repoParts := strings.Split(repo, "/") |
| 155 | if len(repoParts) != 2 || repoParts[0] == "" || repoParts[1] == "" { |
| 156 | return nil, errors.New("repository must be in format 'owner/repo'. Example: github/gh-aw") |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | spec := &RepoSpec{ |
| 161 | RepoSlug: repo, |
| 162 | Version: version, |
| 163 | } |
| 164 | |
| 165 | specLog.Printf("Parsed repo spec successfully: repo=%s, version=%s", repo, version) |
| 166 | return spec, nil |