checkRemoteSymlink checks if a path in a remote GitHub repository is a symlink. Returns the symlink target and true if it is a symlink, or empty string and false otherwise. A nil error with false means the path is not a symlink (e.g., it's a directory or file).
(client *api.RESTClient, owner, repo, dirPath, ref string)
| 749 | // Returns the symlink target and true if it is a symlink, or empty string and false otherwise. |
| 750 | // A nil error with false means the path is not a symlink (e.g., it's a directory or file). |
| 751 | func checkRemoteSymlink(client *api.RESTClient, owner, repo, dirPath, ref string) (string, bool, error) { |
| 752 | endpoint := buildContentsAPIPath(owner, repo, dirPath, ref) |
| 753 | remoteLog.Printf("Checking if path component is symlink: %s/%s/%s@%s", owner, repo, dirPath, ref) |
| 754 | |
| 755 | // The Contents API returns a JSON object for files/symlinks but a JSON array for directories. |
| 756 | // Decode into json.RawMessage first to distinguish these cases without error-driven control flow. |
| 757 | var raw json.RawMessage |
| 758 | err := client.Get(endpoint, &raw) |
| 759 | if err != nil { |
| 760 | remoteLog.Printf("Contents API error for %s: %v", dirPath, err) |
| 761 | return "", false, err |
| 762 | } |
| 763 | |
| 764 | // If the response is an array, this is a directory listing — not a symlink |
| 765 | trimmed := strings.TrimSpace(string(raw)) |
| 766 | if trimmed != "" && trimmed[0] == '[' { |
| 767 | remoteLog.Printf("Path component %s is a directory (not a symlink)", dirPath) |
| 768 | return "", false, nil |
| 769 | } |
| 770 | |
| 771 | // Parse the object response to check the type |
| 772 | var result struct { |
| 773 | Type string `json:"type"` |
| 774 | Target string `json:"target"` |
| 775 | } |
| 776 | if err := json.Unmarshal(raw, &result); err != nil { |
| 777 | return "", false, fmt.Errorf("failed to parse contents response for %s: %w", dirPath, err) |
| 778 | } |
| 779 | |
| 780 | if result.Type == "symlink" && result.Target != "" { |
| 781 | remoteLog.Printf("Path component %s is a symlink -> %s", dirPath, result.Target) |
| 782 | return result.Target, true, nil |
| 783 | } |
| 784 | |
| 785 | remoteLog.Printf("Path component %s is type=%s (not a symlink)", dirPath, result.Type) |
| 786 | return "", false, nil |
| 787 | } |
| 788 | |
| 789 | // resolveRemoteSymlinks resolves symlinks in a remote GitHub repository path. |
| 790 | // The GitHub Contents API doesn't follow symlinks in path components. For example, |