在开源项目中,Issues是一个强大的功能,用于跟踪bug、功能请求和任务。然而,随着项目的发展,Issues可能会变得难以管理,特别是当你需要离线访问或进行深入分析时。
当然GitHub Issues除了上述功能以外,做在线笔记也非常的方便,它支持Markdown语法,还能打标签、分层等等,还天然的支持评论功能。由于我本人就非常热衷于使用Issue做笔记,但是问题就是在离线环境下无法使用,那么能不能把Issue作为离线Markdown文件下载到本地呢?
答案显然是可以的,也是我本次项目的主要功能之一。
issue2file
是一个用Go语言编写的命令行工具,它可以将GitHub仓库中的Issues导出为本地Markdown文件,并提供多种强大的功能:
1)完整内容保留:保留Issue的标题、内容、标签、状态、创建时间等信息
2)评论支持:可选择性地下载Issue的所有评论
除此之外,我想了想能不能通过AI扩展一下?答案显然也是可以的,所以还补充了如下功能:
3)AI分析:集成AI功能,可以对Issues进行智能分析和总结
4)数据可视化:自动生成多种图表,包括Issue状态分布、标签分布和时间趋势等
项目介绍
还是先说一下使用方式吧,当然如果想要支持私有仓库和AI功能的话,需要拿到自己的Github Token和DeepSeek的API Token。
1)仓库地址
https://github.com/ibarryyan/issue2file
2)工具构建
go mod tidy
go build
3)使用方式
最简单的使用方式就是直接运行可执行文件,加上仓库链接作为参数,比如
./issue2file ibarryyan/golang-tips-100
如果想要拉取自己的私有仓库就要先生成一个自己的Github Token,然后使用命令行参数或者配置文件进行启动,详细说明可以参考:
https://github.com/ibarryyan/issue2file/blob/master/README.md
当然了,还有额外的参数能支持生成图表分析仓库的所有Issue,主要有Issue创建时间趋势、状态分析和标签分布等几个维度:
技术实现
issue2file
采用模块化设计,主要包含以下几个核心组件:
1)配置管理:使用Viper库处理配置文件
2)GitHub API交互:使用go-github库获取Issues数据
3)Markdown生成:将Issue数据转换为Markdown格式
4)AI分析:集成AI能力对Issues进行分析
5)图表生成:使用go-echarts库生成数据可视化图表
关键技术点
1)配置管理
项目使用Viper库来处理配置,支持从配置文件中读取各种参数:
func InitConfig() {viper.SetConfigName("config")viper.SetConfigType("conf")viper.AddConfigPath(".")if err := viper.ReadInConfig(); err != nil {log.Fatalf("Error reading config file: %s", err)}// 读取配置项Config.GitHubToken = viper.GetString("gitHubToken")Config.AIToken = viper.GetString("aiToken")Config.CommentEnable = viper.GetBool("commentEnable")Config.AIEnable = viper.GetBool("aiEnable")Config.ChartEnable = viper.GetBool("chartEnable")Config.OutputDir = viper.GetString("outputDir")Config.SummaryFile = viper.GetString("summaryFile")
}
2)GitHub API交互
使用go-github库与GitHub API进行交互,获取Issues数据:
func FetchIssues(owner, repo string) ([]*github.Issue, error) {ctx := context.Background()ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: Config.GitHubToken},)tc := oauth2.NewClient(ctx, ts)client := github.NewClient(tc)opt := &github.IssueListByRepoOptions{State: "all",Sort: "created",Direction: "desc",ListOptions: github.ListOptions{PerPage: 100,},}var allIssues []*github.Issuefor {issues, resp, err := client.Issues.ListByRepo(ctx, owner, repo, opt)if err != nil {return nil, err}allIssues = append(allIssues, issues...)if resp.NextPage == 0 {break}opt.Page = resp.NextPage}return allIssues, nil
}
3)Markdown生成
将Issue数据转换为结构化的Markdown文件:
func GenerateMarkdown(issue *github.Issue, comments []*github.IssueComment) string {var md strings.Builder// 添加标题md.WriteString(fmt.Sprintf("# %s\n\n", *issue.Title))// 添加元数据md.WriteString(fmt.Sprintf("- **Issue编号**: #%d\n", *issue.Number))md.WriteString(fmt.Sprintf("- **创建者**: %s\n", *issue.User.Login))md.WriteString(fmt.Sprintf("- **创建时间**: %s\n", issue.CreatedAt.Format("2006-01-02 15:04:05")))md.WriteString(fmt.Sprintf("- **状态**: %s\n", *issue.State))// 添加标签if len(issue.Labels) > 0 {md.WriteString("- **标签**: ")for i, label := range issue.Labels {if i > 0 {md.WriteString(", ")}md.WriteString(*label.Name)}md.WriteString("\n")}// 添加正文md.WriteString("\n## 内容\n\n")md.WriteString(*issue.Body)// 添加评论if len(comments) > 0 {md.WriteString("\n\n## 评论\n\n")for _, comment := range comments {md.WriteString(fmt.Sprintf("### %s 评论于 %s\n\n", *comment.User.Login, comment.CreatedAt.Format("2006-01-02 15:04:05")))md.WriteString(*comment.Body)md.WriteString("\n\n---\n\n")}}return md.String()
}
4)AI分析
集成AI能力,对Issues进行智能分析和总结:
func AnalyzeIssues(issues []*github.Issue) (string, error) {if !Config.AIEnable || Config.AIToken == "" {return "", errors.New("AI analysis is disabled or token is not provided")}// 准备AI分析的输入数据var input strings.Builderinput.WriteString("请分析以下GitHub Issues,并提供总结报告:\n\n")for _, issue := range issues {input.WriteString(fmt.Sprintf("Issue #%d: %s\n", *issue.Number, *issue.Title))input.WriteString(fmt.Sprintf("状态: %s\n", *issue.State))input.WriteString(fmt.Sprintf("创建时间: %s\n", issue.CreatedAt.Format("2006-01-02")))input.WriteString("标签: ")for i, label := range issue.Labels {if i > 0 {input.WriteString(", ")}input.WriteString(*label.Name)}input.WriteString("\n\n")// 限制内容长度,避免超出AI API的限制body := *issue.Bodyif len(body) > 500 {body = body[:500] + "..."}input.WriteString(body)input.WriteString("\n\n---\n\n")}// 调用AI API进行分析analysis, err := callAIAPI(input.String())if err != nil {return "", err}return analysis, nil
}
5)图表生成
使用go-echarts库生成数据可视化图表:
func GenerateCharts(issues []*github.Issue, outputDir string) error {if !Config.ChartEnable {return nil}// 生成状态分布图if err := generateStatusChart(issues, outputDir); err != nil {return err}// 生成标签分布图if err := generateTagsChart(issues, outputDir); err != nil {return err}// 生成时间趋势图if err := generateTimeChart(issues, outputDir); err != nil {return err}return nil
}
全部代码目前已经开源,大家可以去Github上拉取~~
开源成果
issue2file
项目在开源后的一周内就获得了29个star,这种积极的社区反馈不仅验证了项目的价值,也为未来的发展提供了动力。
在这里主要感谢@ruanyf老师在《科技爱好者周刊》中的推荐!
总结与规划
issue2file
成功地实现了将GitHub Issues转换为本地Markdown文件的核心功能,并通过AI分析和数据可视化等特性提供了额外的价值。
基于社区反馈和项目愿景,未来的发展计划包括:
功能增强:
- 添加增量更新功能,只下载新的或更新的Issues
- 增强AI分析能力,提供更深入的洞察
用户体验改进:
- 提供Web界面,使非技术用户也能轻松使用
- 添加进度显示和更详细的日志
- 完善文档和示例
如果你对这个项目感兴趣,欢迎访问GitHub仓库(https://github.com/ibarryyan/issue2file
),给项目点个star,或者贡献代码!