package github import ( "context" "encoding/base64" "encoding/json" "fmt" "net/url" "strings" "gitea.weiker.me/rodin/review-bot/vcs" ) // GetFileContent fetches a file from the default branch of a repo. // Delegates to GetFileContentAtRef with an empty ref. func (c *Client) GetFileContent(ctx context.Context, owner, repo, path, ref string) (string, error) { return c.GetFileContentAtRef(ctx, owner, repo, path, ref) } // ListContents lists files and directories at a given path in a repo. // Returns the directory listing from the GitHub contents API. func (c *Client) ListContents(ctx context.Context, owner, repo, path string) ([]vcs.ContentEntry, error) { reqURL := fmt.Sprintf("%s/repos/%s/%s/contents/%s", c.baseURL, url.PathEscape(owner), url.PathEscape(repo), escapePath(path)) body, err := c.doGet(ctx, reqURL) if err != nil { return nil, fmt.Errorf("list contents %s: %w", path, err) } var entries []struct { Name string `json:"name"` Path string `json:"path"` Type string `json:"type"` } if err := json.Unmarshal(body, &entries); err != nil { return nil, fmt.Errorf("parse contents JSON: %w", err) } result := make([]vcs.ContentEntry, len(entries)) for i, e := range entries { result[i] = vcs.ContentEntry{ Name: e.Name, Path: e.Path, Type: e.Type, } } return result, nil } // escapePath escapes each segment of a relative file path for use in URLs. // Slashes are preserved as path separators; other special characters are escaped. func escapePath(p string) string { parts := strings.Split(p, "/") for i, part := range parts { parts[i] = url.PathEscape(part) } return strings.Join(parts, "/") } // decodeBase64Content decodes base64-encoded content from the GitHub contents API. // GitHub returns base64 content with newlines for formatting, which we strip before decoding. func decodeBase64Content(encoded string) (string, error) { // GitHub inserts newlines in base64 content cleaned := strings.ReplaceAll(encoded, "\n", "") decoded, err := base64.StdEncoding.DecodeString(cleaned) if err != nil { return "", err } return string(decoded), nil }