又开发了一个优雅的小工具!

发布于:2025-08-03 ⋅ 阅读:(13) ⋅ 点赞:(0)

在开源项目中,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.Issue
    for {
        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.Builder
    input.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.Body
        if 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,或者贡献代码!


网站公告

今日签到

点亮在社区的每一天
去签到