git-markdown 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/AGENT.md +47 -0
- data/CHANGELOG.md +11 -2
- data/Gemfile +0 -2
- data/README.md +18 -16
- data/Rakefile +76 -66
- data/git-markdown.gemspec +3 -0
- data/lib/git/markdown/api/client.rb +11 -0
- data/lib/git/markdown/configuration.rb +11 -1
- data/lib/git/markdown/markdown/generator.rb +2 -2
- data/lib/git/markdown/models/comment.rb +6 -0
- data/lib/git/markdown/providers/github/graphql.rb +95 -0
- data/lib/git/markdown/providers/github.rb +35 -6
- data/lib/git/markdown/version.rb +1 -1
- data/lib/git_markdown.rb +1 -0
- metadata +8 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 76a5b1c374e3b1570dbc81b878c599fbe4948bb82f34ac3e7b83b838fba9f8fa
|
|
4
|
+
data.tar.gz: e59928ca3d2c0e06662c1c3bd5de7a9e6a8dd08fe185107902442a9dc96644aa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 06eca733ed11a7e397f050a3e12080c0354e12c4fe1f760dec9e0237a919e5eaa4d5b65af451356ffceed818c095d0829908f5feb0df0b7eac2b5408e34bb5ff
|
|
7
|
+
data.tar.gz: 0545bb27e46d36a396c5963fbefc4f37a9c00b96ae275872cb0c959e9b3724aa994dc0078c58c900d0b8594f0ba035d4749d876d48d6cb2ac7db286dc62327b2
|
data/AGENT.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Agent Guide
|
|
2
|
+
|
|
3
|
+
Repository-specific rules for code-generation agents working on `git-markdown`.
|
|
4
|
+
|
|
5
|
+
## Core workflow
|
|
6
|
+
|
|
7
|
+
- Prefer surgical edits. Do not reformat unrelated code or reshuffle files.
|
|
8
|
+
- Read the actual call sites and CLI flows before changing behavior.
|
|
9
|
+
- Preserve the public CLI and generated Markdown shape unless the task explicitly requires a change.
|
|
10
|
+
- Fix root causes, not symptoms.
|
|
11
|
+
- Keep one canonical owner per behavior:
|
|
12
|
+
- remote parsing in `Git::Markdown::RemoteParser`
|
|
13
|
+
- auth/config in `Git::Markdown::Configuration` and `Git::Markdown::Credentials`
|
|
14
|
+
- provider fetching in `Git::Markdown::Providers::*`
|
|
15
|
+
- HTTP transport/response handling in `Git::Markdown::Api::*`
|
|
16
|
+
- output rendering in `Git::Markdown::Markdown::Generator`
|
|
17
|
+
|
|
18
|
+
## Naming and code quality
|
|
19
|
+
|
|
20
|
+
- Use one canonical term per concept across code, config, and docs.
|
|
21
|
+
- Do not add parallel wrappers or aliases for the same concept.
|
|
22
|
+
- Use clear names that describe the domain object or action; avoid vague names like `Manager`, `Helper`, or `Handler`.
|
|
23
|
+
- Keep classes small and intention-revealing. If logic starts spanning transport, parsing, and rendering concerns, split by responsibility rather than adding conditionals.
|
|
24
|
+
- Fail fast on unexpected states. Prefer explicit errors over silent fallbacks.
|
|
25
|
+
- When tests are hard to write, simplify the production design first instead of adding test-only indirection.
|
|
26
|
+
|
|
27
|
+
## Testing and boundaries
|
|
28
|
+
|
|
29
|
+
- Framework: Minitest.
|
|
30
|
+
- Keep WebMock enabled. Do not allow live HTTP in tests.
|
|
31
|
+
- Use VCR for outbound GitHub API interactions when fixtures are needed.
|
|
32
|
+
- Add or update minimal tests when behavior changes, especially for CLI output, parsing, and provider interactions.
|
|
33
|
+
- Use `standardrb` for Ruby style consistency.
|
|
34
|
+
- Run `bundle exec rake` before finishing.
|
|
35
|
+
|
|
36
|
+
## Git commits and release
|
|
37
|
+
|
|
38
|
+
- Conventional Commits are required and enforced by `commitlint`.
|
|
39
|
+
- Allowed commit types for normal work: `feat`, `fix`, `refactor`, `chore`, `test`, `docs`.
|
|
40
|
+
- Subject line: imperative, <= 80 chars, no trailing period.
|
|
41
|
+
- Add a body for non-trivial changes explaining why and how.
|
|
42
|
+
- Install hooks once per clone: `bundle exec lefthook install`
|
|
43
|
+
- Release flow:
|
|
44
|
+
- `bundle exec rake 'release:prepare[patch]'`
|
|
45
|
+
- changelog is generated by `git-cliff`
|
|
46
|
+
- publishing happens only in GitHub Actions
|
|
47
|
+
- this gem publishes only to RubyGems
|
data/CHANGELOG.md
CHANGED
|
@@ -5,9 +5,18 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [0.1.
|
|
8
|
+
## [0.1.9] - 2026-04-21
|
|
9
9
|
|
|
10
10
|
### <!-- 1 -->🐛 Bug Fixes
|
|
11
|
-
-
|
|
11
|
+
- publish built gem in release workflow
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### <!-- 3 -->📚 Documentation
|
|
15
|
+
- update gemspec and readme
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### <!-- 7 -->⚙️ Miscellaneous Tasks
|
|
19
|
+
- standardize gem release workflow and repo conventions
|
|
20
|
+
- stop using github variables
|
|
12
21
|
|
|
13
22
|
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -130,7 +130,7 @@ Note: `Gemfile.lock` is intentionally not tracked to avoid conflicts across Ruby
|
|
|
130
130
|
|
|
131
131
|
### Git hooks
|
|
132
132
|
|
|
133
|
-
We use [lefthook](https://lefthook.dev/) with the Ruby [commitlint](https://github.com/arandilopez/commitlint) gem to enforce Conventional Commits on every commit.
|
|
133
|
+
We use [lefthook](https://lefthook.dev/) with the Ruby [commitlint](https://github.com/arandilopez/commitlint) gem to enforce Conventional Commits on every commit. We also use [Standard Ruby](https://standardrb.com/) to keep code style consistent. CI validates commit messages, Standard Ruby, tests, and git-cliff changelog generation on pull requests and pushes to main/master.
|
|
134
134
|
|
|
135
135
|
Run the hook installer once per clone:
|
|
136
136
|
|
|
@@ -146,27 +146,29 @@ rake install
|
|
|
146
146
|
|
|
147
147
|
## Release
|
|
148
148
|
|
|
149
|
-
Releases are
|
|
149
|
+
Releases are tag-driven and published by GitHub Actions to RubyGems. Local release commands never publish directly.
|
|
150
150
|
|
|
151
|
-
|
|
151
|
+
Install [git-cliff](https://git-cliff.org/) locally before preparing a release. The release task regenerates `CHANGELOG.md` from Conventional Commits.
|
|
152
152
|
|
|
153
|
-
|
|
153
|
+
Before preparing a release, make sure you are on `main` or `master` with a clean worktree.
|
|
154
154
|
|
|
155
|
-
|
|
155
|
+
Then run one of:
|
|
156
156
|
|
|
157
157
|
```bash
|
|
158
|
-
bundle
|
|
158
|
+
bundle exec rake 'release:prepare[patch]'
|
|
159
|
+
bundle exec rake 'release:prepare[minor]'
|
|
160
|
+
bundle exec rake 'release:prepare[major]'
|
|
161
|
+
bundle exec rake 'release:prepare[0.1.0]'
|
|
159
162
|
```
|
|
160
163
|
|
|
161
|
-
|
|
164
|
+
The task will:
|
|
162
165
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
+
1. Regenerate `CHANGELOG.md` with `git-cliff`.
|
|
167
|
+
1. Update `lib/git/markdown/version.rb`.
|
|
168
|
+
1. Commit the release changes.
|
|
169
|
+
1. Create and push the `vX.Y.Z` tag.
|
|
166
170
|
|
|
167
|
-
|
|
168
|
-
bundle exec rake release:prep
|
|
169
|
-
```
|
|
171
|
+
The `Release` workflow then runs tests, publishes the gem to RubyGems, and creates the GitHub release from the changelog entry.
|
|
170
172
|
|
|
171
173
|
## Contributing
|
|
172
174
|
|
|
@@ -199,7 +201,7 @@ MIT License, see [LICENSE.txt](LICENSE.txt)
|
|
|
199
201
|
|
|
200
202
|
## About
|
|
201
203
|
|
|
202
|
-
Made by the team at [Ethos Link](https://www.ethos-link.com) — practical software for growing businesses. We build tools for hospitality
|
|
204
|
+
Made by the team at [Ethos Link](https://www.ethos-link.com) — practical software for growing businesses. We build tools for hospitality operators who need clear workflows, fast onboarding, and real human support.
|
|
203
205
|
|
|
204
|
-
We also build [Reviato](https://www.reviato.com), “
|
|
205
|
-
|
|
206
|
+
We also build [Reviato](https://www.reviato.com), “Capture. Interpret. Act.”.
|
|
207
|
+
Turn guest feedback into clear next steps for your team. Collect private appraisals, spot patterns across reviews, and act before small issues turn into public ones.
|
data/Rakefile
CHANGED
|
@@ -1,89 +1,99 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Note: Releases are handled automatically by GitHub Actions when you push a tag (e.g., v0.2.0)
|
|
4
|
-
# See .github/workflows/release.yml for details
|
|
5
3
|
require "bundler/gem_tasks"
|
|
6
4
|
require "rake/testtask"
|
|
7
5
|
require "standard/rake"
|
|
8
6
|
require_relative "lib/git/markdown/version"
|
|
9
7
|
|
|
8
|
+
VERSION_PATH = File.expand_path("lib/git/markdown/version.rb", __dir__)
|
|
9
|
+
VALID_RELEASE_TARGETS = %w[major minor patch].freeze
|
|
10
|
+
|
|
10
11
|
Rake::TestTask.new(:test) do |t|
|
|
11
12
|
t.libs << "test"
|
|
12
13
|
t.libs << "lib"
|
|
13
14
|
t.test_files = FileList["test/**/*_test.rb"]
|
|
14
15
|
end
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
def current_branch
|
|
18
|
+
`git branch --show-current`.strip
|
|
19
|
+
end
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
def clean_worktree?
|
|
22
|
+
system("git diff --quiet") && system("git diff --cached --quiet")
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def release_version(target)
|
|
26
|
+
target = target.to_s.strip
|
|
27
|
+
raise ArgumentError, "Provide patch, minor, major, or an explicit X.Y.Z version." if target.empty?
|
|
28
|
+
|
|
29
|
+
return target if target.match?(/\A\d+\.\d+\.\d+\z/)
|
|
30
|
+
|
|
31
|
+
unless VALID_RELEASE_TARGETS.include?(target)
|
|
32
|
+
raise ArgumentError, "Invalid release target #{target.inspect}. Use #{VALID_RELEASE_TARGETS.join(", ")} or X.Y.Z."
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
major, minor, patch = GitMarkdown::VERSION.split(".").map(&:to_i)
|
|
36
|
+
|
|
37
|
+
case target
|
|
38
|
+
when "major"
|
|
39
|
+
"#{major + 1}.0.0"
|
|
40
|
+
when "minor"
|
|
41
|
+
"#{major}.#{minor + 1}.0"
|
|
42
|
+
when "patch"
|
|
43
|
+
"#{major}.#{minor}.#{patch + 1}"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def update_version_file(version)
|
|
48
|
+
File.write(
|
|
49
|
+
VERSION_PATH,
|
|
50
|
+
<<~RUBY
|
|
51
|
+
# frozen_string_literal: true
|
|
52
|
+
|
|
53
|
+
module GitMarkdown
|
|
54
|
+
VERSION = "#{version}"
|
|
55
|
+
end
|
|
56
|
+
RUBY
|
|
57
|
+
)
|
|
58
|
+
end
|
|
23
59
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
60
|
+
def update_changelog(version)
|
|
61
|
+
success = system("git-cliff", "-c", "cliff.toml", "--unreleased", "--tag", "v#{version}", "-o", "CHANGELOG.md")
|
|
62
|
+
raise "git-cliff failed. Install git-cliff and make sure cliff.toml is valid." unless success
|
|
63
|
+
raise "git-cliff did not update CHANGELOG.md. Ensure there are Conventional Commits since the last tag." if system("git", "diff", "--quiet", "--", "CHANGELOG.md")
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
if Rake::Task.task_defined?("release")
|
|
67
|
+
Rake::Task["release"].clear
|
|
68
|
+
end
|
|
27
69
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
70
|
+
desc "Publishing is handled by GitHub Actions. Use release:prepare[...] instead."
|
|
71
|
+
task :release do
|
|
72
|
+
abort "Use `bundle exec rake 'release:prepare[patch]'` (or minor/major/X.Y.Z). Publishing runs in GitHub Actions after the tag is pushed."
|
|
73
|
+
end
|
|
32
74
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
75
|
+
namespace :release do
|
|
76
|
+
desc "Prepare a release: update CHANGELOG/version, commit, tag, and push. Accepts patch, minor, major, or X.Y.Z."
|
|
77
|
+
task :prepare, [:target] do |_task, args|
|
|
78
|
+
branch = current_branch
|
|
79
|
+
abort "Release must run on main or master. Current branch: #{branch.inspect}." unless %w[main master].include?(branch)
|
|
80
|
+
abort "Release requires a clean working tree." unless clean_worktree?
|
|
36
81
|
|
|
82
|
+
version = release_version(args[:target])
|
|
37
83
|
current = GitMarkdown::VERSION
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
next_version = case level
|
|
41
|
-
when "major"
|
|
42
|
-
"#{parts[0] + 1}.0.0"
|
|
43
|
-
when "minor"
|
|
44
|
-
"#{parts[0]}.#{parts[1] + 1}.0"
|
|
45
|
-
when "patch"
|
|
46
|
-
"#{parts[0]}.#{parts[1]}.#{parts[2] + 1}"
|
|
47
|
-
when "pre"
|
|
48
|
-
"#{parts[0]}.#{parts[1]}.#{parts[2]}.pre.1"
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
puts "=== Step 1: Updating CHANGELOG.md for v#{next_version} ==="
|
|
52
|
-
sh "git cliff -c cliff.toml --unreleased --tag v#{next_version} -o CHANGELOG.md"
|
|
53
|
-
|
|
54
|
-
if system("git diff --quiet -- CHANGELOG.md")
|
|
55
|
-
puts "Warning: No changelog changes detected"
|
|
56
|
-
else
|
|
57
|
-
puts "✓ Changelog updated"
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
puts "\n=== Step 2: Bumping version to #{next_version} ==="
|
|
61
|
-
|
|
62
|
-
File.write(
|
|
63
|
-
"lib/git/markdown/version.rb",
|
|
64
|
-
<<~RUBY
|
|
65
|
-
# frozen_string_literal: true
|
|
66
|
-
|
|
67
|
-
module GitMarkdown
|
|
68
|
-
VERSION = "#{next_version}"
|
|
69
|
-
end
|
|
70
|
-
RUBY
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
puts "✓ Version updated in lib/git/markdown/version.rb"
|
|
74
|
-
|
|
75
|
-
puts "\n=== Step 3: Committing changes ==="
|
|
76
|
-
sh "git add CHANGELOG.md lib/git/markdown/version.rb"
|
|
77
|
-
sh "git commit -m \"chore(release): bump version to #{next_version}\""
|
|
78
|
-
puts "✓ Changes committed"
|
|
84
|
+
abort "Release version #{version} is older than current version #{current}." if Gem::Version.new(version) < Gem::Version.new(current)
|
|
79
85
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
sh "git push origin master"
|
|
83
|
-
sh "git push origin v#{next_version}"
|
|
84
|
-
puts "✓ Tag v#{next_version} created and pushed"
|
|
86
|
+
update_changelog(version)
|
|
87
|
+
update_version_file(version)
|
|
85
88
|
|
|
86
|
-
|
|
87
|
-
|
|
89
|
+
sh "git add CHANGELOG.md lib/git/markdown/version.rb"
|
|
90
|
+
sh %(git commit -m "chore(release): prepare v#{version}")
|
|
91
|
+
sh %(git tag -a v#{version} -m "Release v#{version}")
|
|
92
|
+
sh "git push origin #{branch}"
|
|
93
|
+
sh "git push origin v#{version}"
|
|
94
|
+
rescue ArgumentError, RuntimeError => e
|
|
95
|
+
abort e.message
|
|
88
96
|
end
|
|
89
97
|
end
|
|
98
|
+
|
|
99
|
+
task default: %i[test standard]
|
data/git-markdown.gemspec
CHANGED
|
@@ -6,6 +6,7 @@ Gem::Specification.new do |spec|
|
|
|
6
6
|
spec.name = "git-markdown"
|
|
7
7
|
spec.version = GitMarkdown::VERSION
|
|
8
8
|
spec.authors = ["Paulo Fidalgo", "Ethos Link"]
|
|
9
|
+
spec.email = ["devel@ethos-link.com"]
|
|
9
10
|
|
|
10
11
|
spec.summary = "Convert GitHub PRs to Markdown for local AI code review"
|
|
11
12
|
spec.description = "A CLI tool that fetches GitHub pull requests and converts them to Markdown format, perfect for local AI assistants like opencode and codex."
|
|
@@ -23,6 +24,8 @@ Gem::Specification.new do |spec|
|
|
|
23
24
|
"changelog_uri" => "#{repo}/blob/#{branch}/CHANGELOG.md",
|
|
24
25
|
"documentation_uri" => "#{repo}/blob/#{branch}/README.md",
|
|
25
26
|
"funding_uri" => "https://www.reviato.com/",
|
|
27
|
+
"github_repo" => "ssh://github.com/ethos-link/git-markdown",
|
|
28
|
+
"allowed_push_host" => "https://rubygems.org",
|
|
26
29
|
"rubygems_mfa_required" => "true"
|
|
27
30
|
}
|
|
28
31
|
|
|
@@ -17,6 +17,17 @@ module GitMarkdown
|
|
|
17
17
|
Response.new(response)
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
+
def post(path, body = {})
|
|
21
|
+
uri = build_uri(path, {})
|
|
22
|
+
request = Net::HTTP::Post.new(uri)
|
|
23
|
+
set_headers(request)
|
|
24
|
+
request["Content-Type"] = "application/json"
|
|
25
|
+
request.body = JSON.generate(body)
|
|
26
|
+
|
|
27
|
+
response = http_request(uri, request)
|
|
28
|
+
Response.new(response)
|
|
29
|
+
end
|
|
30
|
+
|
|
20
31
|
private
|
|
21
32
|
|
|
22
33
|
def build_uri(path, params)
|
|
@@ -7,13 +7,14 @@ module GitMarkdown
|
|
|
7
7
|
XDG_CONFIG_HOME = ENV.fetch("XDG_CONFIG_HOME", File.expand_path("~/.config"))
|
|
8
8
|
DEFAULT_PROVIDER = :github
|
|
9
9
|
|
|
10
|
-
attr_accessor :token, :provider, :api_url, :output_dir, :default_status
|
|
10
|
+
attr_accessor :token, :provider, :api_url, :graphql_url, :output_dir, :default_status
|
|
11
11
|
|
|
12
12
|
def initialize
|
|
13
13
|
@provider = DEFAULT_PROVIDER
|
|
14
14
|
@output_dir = Dir.pwd
|
|
15
15
|
@default_status = :unresolved
|
|
16
16
|
@api_url = nil
|
|
17
|
+
@graphql_url = nil
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
def self.load
|
|
@@ -24,6 +25,7 @@ module GitMarkdown
|
|
|
24
25
|
load_from_file if config_file_exist?
|
|
25
26
|
resolve_credentials
|
|
26
27
|
resolve_api_url if api_url.nil?
|
|
28
|
+
resolve_graphql_url if graphql_url.nil?
|
|
27
29
|
self
|
|
28
30
|
end
|
|
29
31
|
|
|
@@ -67,6 +69,7 @@ module GitMarkdown
|
|
|
67
69
|
)
|
|
68
70
|
@provider = config[:provider] if config[:provider]
|
|
69
71
|
@api_url = config[:api_url] if config[:api_url]
|
|
72
|
+
@graphql_url = config[:graphql_url] if config[:graphql_url]
|
|
70
73
|
@output_dir = config[:output_dir] if config[:output_dir]
|
|
71
74
|
@default_status = config[:default_status].to_sym if config[:default_status]
|
|
72
75
|
end
|
|
@@ -81,10 +84,17 @@ module GitMarkdown
|
|
|
81
84
|
end
|
|
82
85
|
end
|
|
83
86
|
|
|
87
|
+
def resolve_graphql_url
|
|
88
|
+
@graphql_url = ENV.fetch("GITHUB_GRAPHQL_URL") do
|
|
89
|
+
(@provider == :github) ? "https://api.github.com/graphql" : nil
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
84
93
|
def config_to_yaml
|
|
85
94
|
{
|
|
86
95
|
provider: @provider,
|
|
87
96
|
api_url: @api_url,
|
|
97
|
+
graphql_url: @graphql_url,
|
|
88
98
|
output_dir: @output_dir,
|
|
89
99
|
default_status: @default_status
|
|
90
100
|
}.to_yaml
|
|
@@ -50,9 +50,9 @@ module GitMarkdown
|
|
|
50
50
|
def include_comment?(comment)
|
|
51
51
|
case @status_filter
|
|
52
52
|
when :unresolved
|
|
53
|
-
!comment.body.include?("[resolved]") && !comment.body.include?("[done]")
|
|
53
|
+
!comment.resolved? && !comment.body.include?("[resolved]") && !comment.body.include?("[done]")
|
|
54
54
|
when :resolved
|
|
55
|
-
comment.body.include?("[resolved]") || comment.body.include?("[done]")
|
|
55
|
+
comment.resolved? || comment.body.include?("[resolved]") || comment.body.include?("[done]")
|
|
56
56
|
else
|
|
57
57
|
true
|
|
58
58
|
end
|
|
@@ -4,6 +4,7 @@ module GitMarkdown
|
|
|
4
4
|
module Models
|
|
5
5
|
class Comment
|
|
6
6
|
attr_reader :id, :body, :author, :path, :line, :html_url, :created_at, :updated_at, :in_reply_to_id
|
|
7
|
+
attr_accessor :resolved
|
|
7
8
|
|
|
8
9
|
def initialize(attrs = {})
|
|
9
10
|
@id = attrs[:id]
|
|
@@ -15,6 +16,7 @@ module GitMarkdown
|
|
|
15
16
|
@created_at = attrs[:created_at]
|
|
16
17
|
@updated_at = attrs[:updated_at]
|
|
17
18
|
@in_reply_to_id = attrs[:in_reply_to_id]
|
|
19
|
+
@resolved = attrs.fetch(:resolved, false)
|
|
18
20
|
end
|
|
19
21
|
|
|
20
22
|
def self.from_api(data)
|
|
@@ -38,6 +40,10 @@ module GitMarkdown
|
|
|
38
40
|
def reply?
|
|
39
41
|
!@in_reply_to_id.nil?
|
|
40
42
|
end
|
|
43
|
+
|
|
44
|
+
def resolved?
|
|
45
|
+
@resolved == true
|
|
46
|
+
end
|
|
41
47
|
end
|
|
42
48
|
end
|
|
43
49
|
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GitMarkdown
|
|
4
|
+
module Providers
|
|
5
|
+
class GitHub
|
|
6
|
+
class Graphql
|
|
7
|
+
RESOLVED_STATE_QUERY = <<~GRAPHQL
|
|
8
|
+
query($owner: String!, $repo: String!, $number: Int!, $threadsAfter: String, $commentsAfter: String) {
|
|
9
|
+
repository(owner: $owner, name: $repo) {
|
|
10
|
+
pullRequest(number: $number) {
|
|
11
|
+
reviewThreads(first: 100, after: $threadsAfter) {
|
|
12
|
+
pageInfo {
|
|
13
|
+
hasNextPage
|
|
14
|
+
endCursor
|
|
15
|
+
}
|
|
16
|
+
nodes {
|
|
17
|
+
isResolved
|
|
18
|
+
comments(first: 100, after: $commentsAfter) {
|
|
19
|
+
pageInfo {
|
|
20
|
+
hasNextPage
|
|
21
|
+
endCursor
|
|
22
|
+
}
|
|
23
|
+
nodes {
|
|
24
|
+
databaseId
|
|
25
|
+
body
|
|
26
|
+
path
|
|
27
|
+
line
|
|
28
|
+
createdAt
|
|
29
|
+
updatedAt
|
|
30
|
+
author {
|
|
31
|
+
login
|
|
32
|
+
}
|
|
33
|
+
replyTo {
|
|
34
|
+
databaseId
|
|
35
|
+
}
|
|
36
|
+
url
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
GRAPHQL
|
|
45
|
+
|
|
46
|
+
def initialize(config)
|
|
47
|
+
@config = config
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def fetch_resolved_states(owner, repo, number)
|
|
51
|
+
resolved_ids = Set.new
|
|
52
|
+
threads_after = nil
|
|
53
|
+
|
|
54
|
+
loop do
|
|
55
|
+
response = client.post("", {
|
|
56
|
+
query: RESOLVED_STATE_QUERY,
|
|
57
|
+
variables: {owner: owner, repo: repo, number: number,
|
|
58
|
+
threadsAfter: threads_after}
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
raise ApiError, "GraphQL request failed: #{response.error_message}" unless response.success?
|
|
62
|
+
|
|
63
|
+
data = response.data
|
|
64
|
+
threads_data = data.dig("data", "repository", "pullRequest", "reviewThreads")
|
|
65
|
+
break unless threads_data
|
|
66
|
+
|
|
67
|
+
threads_data["nodes"].each do |thread|
|
|
68
|
+
next unless thread["isResolved"]
|
|
69
|
+
|
|
70
|
+
thread["comments"]["nodes"].each do |comment|
|
|
71
|
+
resolved_ids.add(comment["databaseId"]) if comment["databaseId"]
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
threads_page = threads_data["pageInfo"]
|
|
76
|
+
break unless threads_page["hasNextPage"]
|
|
77
|
+
|
|
78
|
+
threads_after = threads_page["endCursor"]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
resolved_ids
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
def client
|
|
87
|
+
@client ||= Api::Client.new(
|
|
88
|
+
base_url: @config.graphql_url,
|
|
89
|
+
token: @config.token
|
|
90
|
+
)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -21,23 +21,36 @@ module GitMarkdown
|
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def fetch_reviews(owner, repo, number)
|
|
24
|
+
resolved_ids = fetch_resolved_comment_ids(owner, repo, number)
|
|
25
|
+
|
|
24
26
|
path = "/repos/#{owner}/#{repo}/pulls/#{number}/reviews"
|
|
25
27
|
reviews = fetch_all_pages(path)
|
|
26
28
|
|
|
29
|
+
all_pr_comments = fetch_all_pr_comments(owner, repo, number)
|
|
30
|
+
|
|
27
31
|
reviews.map do |review_data|
|
|
28
32
|
review = Models::Review.from_api(review_data)
|
|
29
|
-
review.comments =
|
|
33
|
+
review.comments = all_pr_comments
|
|
34
|
+
.select { |c| c["pull_request_review_id"] == review.id }
|
|
35
|
+
.map do |data|
|
|
36
|
+
comment = Models::Comment.from_api(data)
|
|
37
|
+
comment.resolved = resolved_ids.include?(comment.id)
|
|
38
|
+
comment
|
|
39
|
+
end
|
|
30
40
|
review
|
|
31
41
|
end
|
|
32
42
|
end
|
|
33
43
|
|
|
34
|
-
def fetch_review_comments(owner, repo, review_id)
|
|
35
|
-
|
|
36
|
-
all_comments = fetch_all_pages(path)
|
|
44
|
+
def fetch_review_comments(owner, repo, review_id, resolved_ids = Set.new)
|
|
45
|
+
all_pr_comments = fetch_all_pr_comments(owner, repo)
|
|
37
46
|
|
|
38
|
-
|
|
47
|
+
all_pr_comments
|
|
39
48
|
.select { |c| c["pull_request_review_id"] == review_id }
|
|
40
|
-
.map
|
|
49
|
+
.map do |data|
|
|
50
|
+
comment = Models::Comment.from_api(data)
|
|
51
|
+
comment.resolved = resolved_ids.include?(comment.id)
|
|
52
|
+
comment
|
|
53
|
+
end
|
|
41
54
|
end
|
|
42
55
|
|
|
43
56
|
private
|
|
@@ -49,6 +62,22 @@ module GitMarkdown
|
|
|
49
62
|
)
|
|
50
63
|
end
|
|
51
64
|
|
|
65
|
+
def graphql_client
|
|
66
|
+
@graphql_client ||= Graphql.new(@config)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def fetch_resolved_comment_ids(owner, repo, number)
|
|
70
|
+
graphql_client.fetch_resolved_states(owner, repo, number)
|
|
71
|
+
rescue ApiError
|
|
72
|
+
Set.new
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def fetch_all_pr_comments(owner, repo, _number = nil)
|
|
76
|
+
@all_pr_comments ||= {}
|
|
77
|
+
key = "#{owner}/#{repo}"
|
|
78
|
+
@all_pr_comments[key] ||= fetch_all_pages("/repos/#{owner}/#{repo}/pulls/comments")
|
|
79
|
+
end
|
|
80
|
+
|
|
52
81
|
def fetch_all_pages(path, params = {})
|
|
53
82
|
results = []
|
|
54
83
|
page = 1
|
data/lib/git/markdown/version.rb
CHANGED
data/lib/git_markdown.rb
CHANGED
|
@@ -15,6 +15,7 @@ require_relative "git/markdown/credentials"
|
|
|
15
15
|
require_relative "git/markdown/remote_parser"
|
|
16
16
|
require_relative "git/markdown/providers/base"
|
|
17
17
|
require_relative "git/markdown/providers/github"
|
|
18
|
+
require_relative "git/markdown/providers/github/graphql"
|
|
18
19
|
require_relative "git/markdown/api/client"
|
|
19
20
|
require_relative "git/markdown/api/response"
|
|
20
21
|
require_relative "git/markdown/models/pull_request"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: git-markdown
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Paulo Fidalgo
|
|
@@ -96,11 +96,14 @@ dependencies:
|
|
|
96
96
|
version: '3.0'
|
|
97
97
|
description: A CLI tool that fetches GitHub pull requests and converts them to Markdown
|
|
98
98
|
format, perfect for local AI assistants like opencode and codex.
|
|
99
|
+
email:
|
|
100
|
+
- devel@ethos-link.com
|
|
99
101
|
executables:
|
|
100
102
|
- git-markdown
|
|
101
103
|
extensions: []
|
|
102
104
|
extra_rdoc_files: []
|
|
103
105
|
files:
|
|
106
|
+
- AGENT.md
|
|
104
107
|
- AGENTS.md
|
|
105
108
|
- CHANGELOG.md
|
|
106
109
|
- Gemfile
|
|
@@ -123,6 +126,7 @@ files:
|
|
|
123
126
|
- lib/git/markdown/models/review.rb
|
|
124
127
|
- lib/git/markdown/providers/base.rb
|
|
125
128
|
- lib/git/markdown/providers/github.rb
|
|
129
|
+
- lib/git/markdown/providers/github/graphql.rb
|
|
126
130
|
- lib/git/markdown/remote_parser.rb
|
|
127
131
|
- lib/git/markdown/version.rb
|
|
128
132
|
- lib/git_markdown.rb
|
|
@@ -136,6 +140,8 @@ metadata:
|
|
|
136
140
|
changelog_uri: https://github.com/ethos-link/git-markdown/blob/master/CHANGELOG.md
|
|
137
141
|
documentation_uri: https://github.com/ethos-link/git-markdown/blob/master/README.md
|
|
138
142
|
funding_uri: https://www.reviato.com/
|
|
143
|
+
github_repo: ssh://github.com/ethos-link/git-markdown
|
|
144
|
+
allowed_push_host: https://rubygems.org
|
|
139
145
|
rubygems_mfa_required: 'true'
|
|
140
146
|
rdoc_options: []
|
|
141
147
|
require_paths:
|
|
@@ -151,7 +157,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
151
157
|
- !ruby/object:Gem::Version
|
|
152
158
|
version: '0'
|
|
153
159
|
requirements: []
|
|
154
|
-
rubygems_version: 4.0.
|
|
160
|
+
rubygems_version: 4.0.6
|
|
155
161
|
specification_version: 4
|
|
156
162
|
summary: Convert GitHub PRs to Markdown for local AI code review
|
|
157
163
|
test_files: []
|