git-markdown 0.1.0
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 +7 -0
- data/AGENTS.md +248 -0
- data/CHANGELOG.md +31 -0
- data/Gemfile +13 -0
- data/LICENSE.txt +21 -0
- data/README.md +205 -0
- data/Rakefile +48 -0
- data/bin/git-markdown +6 -0
- data/cliff.toml +53 -0
- data/git-markdown.gemspec +34 -0
- data/lefthook.yml +6 -0
- data/lib/git/markdown/api/client.rb +48 -0
- data/lib/git/markdown/api/response.rb +43 -0
- data/lib/git/markdown/cli.rb +153 -0
- data/lib/git/markdown/configuration.rb +93 -0
- data/lib/git/markdown/credentials.rb +70 -0
- data/lib/git/markdown/markdown/generator.rb +66 -0
- data/lib/git/markdown/markdown/templates/default.erb +56 -0
- data/lib/git/markdown/models/comment.rb +43 -0
- data/lib/git/markdown/models/pull_request.rb +41 -0
- data/lib/git/markdown/models/review.rb +47 -0
- data/lib/git/markdown/providers/base.rb +29 -0
- data/lib/git/markdown/providers/github.rb +74 -0
- data/lib/git/markdown/remote_parser.rb +54 -0
- data/lib/git/markdown/version.rb +5 -0
- data/lib/git_markdown.rb +34 -0
- metadata +152 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 8414cb82426c583ac747480c7fd996b39f35c2758356884881667a57df54efe3
|
|
4
|
+
data.tar.gz: ae38f4bf979c40750003cbda6cf65548650c189f929bb33d75e268fe8dcb8e40
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: f4e5fe13c671fc6e7934e3bbbfc28f8c3853f0a3c5f04878b8605c1dbf293c01297eda5d8078d966be2fa86b2be6af0a97dd118e78ecd744c031d39b82ca59a2
|
|
7
|
+
data.tar.gz: 0ec5b02ec1d5a10fd981ef8a1c8732df476a10942dc132d57cdb2e8792c21af5c4f3c9089d92b53d03b9ff2b237c99e9ac899d527e6d1be6bffc266f1f5f23d5
|
data/AGENTS.md
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# AI Agent Playbook
|
|
2
|
+
|
|
3
|
+
Rules any code-generation agent must follow when editing this repository.
|
|
4
|
+
Keep changes minimal, validated, and consistent with existing patterns.
|
|
5
|
+
|
|
6
|
+
## How to work in this repo
|
|
7
|
+
|
|
8
|
+
- Prefer surgical edits. Don’t reformat unrelated code. Preserve public APIs unless required.
|
|
9
|
+
- Source of truth is the code: read actual files, signatures, and call sites before changing anything.
|
|
10
|
+
- Think before acting: understand root causes; don’t treat symptoms.
|
|
11
|
+
|
|
12
|
+
### Stack constraints (do not drift)
|
|
13
|
+
|
|
14
|
+
- Ruby 3.4 + Rails 8.1
|
|
15
|
+
- No-build frontend: Importmap + tailwindcss-rails (Tailwind v4)
|
|
16
|
+
- Use `bin/dev` for local dev (Rails + Tailwind watcher + GoodJob)
|
|
17
|
+
- Do not introduce Node/Yarn or JS bundlers
|
|
18
|
+
|
|
19
|
+
### Commands (run before finishing)
|
|
20
|
+
|
|
21
|
+
- Tests: `bundle exec rails test` (all) or targeted files
|
|
22
|
+
- System tests: `bundle exec rails test:system`
|
|
23
|
+
- Coverage: `COVERAGE=1 bundle exec rails test`
|
|
24
|
+
- Lint: `bin/rubocop`
|
|
25
|
+
- ERB lint: `bundle exec erb_lint --lint-all`
|
|
26
|
+
- Assets: `RAILS_ENV=production bin/rails assets:precompile`
|
|
27
|
+
- Data: `bin/rails data:plans:seed`
|
|
28
|
+
|
|
29
|
+
## Boundaries
|
|
30
|
+
|
|
31
|
+
### Always
|
|
32
|
+
|
|
33
|
+
- Keep diffs surgical; don’t shuffle files or reformat unrelated code.
|
|
34
|
+
- Add/update minimal tests when behavior changes.
|
|
35
|
+
- Use i18n keys for user-facing copy (no hardcoded UI strings).
|
|
36
|
+
- Keep WebMock enabled in tests; do not allow live HTTP.
|
|
37
|
+
|
|
38
|
+
### Ask first (high-risk)
|
|
39
|
+
|
|
40
|
+
- New dependencies (gems / JS packages / tooling).
|
|
41
|
+
- Database migrations or schema changes.
|
|
42
|
+
- Changes to provider contracts, auth/authorization, billing, or security boundaries.
|
|
43
|
+
- Editing Rails credentials / secrets handling.
|
|
44
|
+
|
|
45
|
+
### Never
|
|
46
|
+
|
|
47
|
+
- Commit secrets/credentials, master keys, or decrypted values.
|
|
48
|
+
- Add Node/Yarn/bundlers to the stack.
|
|
49
|
+
- Add debug prints/breakpoints in app/runtime code (`puts`, `pp`, `binding.pry`).
|
|
50
|
+
- Exception: intentional CLI output in Rake tasks is acceptable.
|
|
51
|
+
|
|
52
|
+
## Git commits
|
|
53
|
+
|
|
54
|
+
- Use Conventional Commits.
|
|
55
|
+
- Subject line ≤ 80 chars, imperative, no trailing period.
|
|
56
|
+
- Body (when non-trivial): explain **why** and **how** (not what).
|
|
57
|
+
- Types: `feat`, `fix`, `refactor`, `chore`, `test`, `docs`.
|
|
58
|
+
|
|
59
|
+
## Project map (what goes where)
|
|
60
|
+
|
|
61
|
+
- `app/`: Rails MVC, jobs, services (legacy), views.
|
|
62
|
+
- `lib/`: External providers and infra (`lib/providers`, `lib/faraday`, `lib/rack`).
|
|
63
|
+
- Data in `lib/data/` (e.g., `plans.yml`). Rake tasks in `lib/tasks/`.
|
|
64
|
+
- `lib/providers`: Provider interfaces/results + implementations. Use `Providers::Page`/`Providers::Review` at boundaries.
|
|
65
|
+
- Webhook resources via `Providers::Resources` (`:page`, `:review`).
|
|
66
|
+
- `test/`: Minitest (`integration/`, `system/`, etc.)
|
|
67
|
+
- VCR cassettes: `test/support/cassettes/`
|
|
68
|
+
- Webhook captures: `test/support/webhook_captures/`
|
|
69
|
+
- Controllers: root under `app/controllers/`; namespaced in matching folders.
|
|
70
|
+
- Base classes: `PublicBaseController`, `DashboardBaseController`, `Admin::BaseController`.
|
|
71
|
+
|
|
72
|
+
## Decision trees (use these defaults)
|
|
73
|
+
|
|
74
|
+
### 1) You’re touching an Actor/service object
|
|
75
|
+
|
|
76
|
+
- Is the change trivial (typo, small conditional, tiny refactor)?
|
|
77
|
+
- Yes → keep the existing Actor/service shape.
|
|
78
|
+
- No → convert the touched service to a PORO as part of the change.
|
|
79
|
+
- When converting:
|
|
80
|
+
- Prefer explicit constructors + methods (`ThingCreator.new(...).create`)
|
|
81
|
+
- Or move behavior onto the relevant model when it naturally belongs there.
|
|
82
|
+
|
|
83
|
+
### 2) You’re touching a ViewComponent / adding shared UI
|
|
84
|
+
|
|
85
|
+
- Are you adding new UI/shared UI?
|
|
86
|
+
- Yes → use Rails partials under `app/views/shared/` (layout-aware; see below).
|
|
87
|
+
- Are you touching an existing ViewComponent?
|
|
88
|
+
- Small change → keep it as-is.
|
|
89
|
+
- Meaningful change → prefer migrating it to a partial as part of the change.
|
|
90
|
+
|
|
91
|
+
## Rails conventions
|
|
92
|
+
|
|
93
|
+
- Routing: keep routes simple and close to Rails defaults.
|
|
94
|
+
- Avoid redundant `defaults:` / `controller:` overrides when convention can solve it.
|
|
95
|
+
- Avoid `:as` overrides unless strictly necessary.
|
|
96
|
+
- Prefer `namespace` + `resources`.
|
|
97
|
+
- Prefer REST/CRUD: introduce a resource instead of custom actions.
|
|
98
|
+
|
|
99
|
+
## Code style (scoped, Fizzy-inspired)
|
|
100
|
+
|
|
101
|
+
Directional rules. Apply them to **new code** and **methods/files you already touch**.
|
|
102
|
+
Do not do churn-only refactors solely to “match style”.
|
|
103
|
+
|
|
104
|
+
- Prefer expanded conditionals over guard clauses.
|
|
105
|
+
- Exception: early return at the very start of a method when the main body is non-trivial.
|
|
106
|
+
- Method ordering in classes:
|
|
107
|
+
1) class methods
|
|
108
|
+
2) public methods (with `initialize` first)
|
|
109
|
+
3) private methods
|
|
110
|
+
- Order methods vertically by invocation order (top-down flow).
|
|
111
|
+
- Only use `!` when there is a corresponding non-`!` method.
|
|
112
|
+
- No blank line after visibility modifiers; indent methods under them.
|
|
113
|
+
- Thin controllers invoking rich domain APIs; avoid introducing “service artifacts” as glue.
|
|
114
|
+
- Jobs should be shallow: enqueue via `*_later`, execute logic in `*_now` / domain methods.
|
|
115
|
+
|
|
116
|
+
## Fail fast (avoid defensive programming)
|
|
117
|
+
|
|
118
|
+
We prefer code to break loudly when something unexpected happens.
|
|
119
|
+
|
|
120
|
+
- Avoid “defensive” checks that mask errors:
|
|
121
|
+
- `respond_to?`, `try`, dynamic `send` to avoid using the real API
|
|
122
|
+
- broad `rescue` that swallows exceptions
|
|
123
|
+
- returning nil/false as a fallback for unexpected states
|
|
124
|
+
- Prefer explicit contracts:
|
|
125
|
+
- `find_by!` instead of `find_by` when it must exist
|
|
126
|
+
- `Hash#fetch` when a key must exist
|
|
127
|
+
- Rescue only specific exceptions you truly handle, and keep handling intentional.
|
|
128
|
+
|
|
129
|
+
## Architecture direction
|
|
130
|
+
|
|
131
|
+
### Services (Actor gem) → POROs
|
|
132
|
+
|
|
133
|
+
- Current state: there are service objects using the Actor gem.
|
|
134
|
+
- Direction: migrate toward POROs inline with Rails/DHH style.
|
|
135
|
+
- Do not add new Actor-based services.
|
|
136
|
+
- Use the decision tree above to decide when to migrate.
|
|
137
|
+
|
|
138
|
+
### Views: ViewComponents → Partials
|
|
139
|
+
|
|
140
|
+
- Do not add new ViewComponents.
|
|
141
|
+
- Prefer Rails partials under `app/views/shared/`.
|
|
142
|
+
- Because we have 3 layouts (`public`, `application`, `admin`), prefer layout-aware shared folders:
|
|
143
|
+
- `app/views/shared/public/…`
|
|
144
|
+
- `app/views/shared/application/…`
|
|
145
|
+
- `app/views/shared/admin/…`
|
|
146
|
+
- Pass data via `locals`; avoid relying on instance vars in shared partials.
|
|
147
|
+
|
|
148
|
+
## Coding standards & optimization
|
|
149
|
+
|
|
150
|
+
- Less is more: prefer less code without sacrificing readability.
|
|
151
|
+
- Use clear, intention-revealing names; avoid vague names like `Manager`, `Handler`.
|
|
152
|
+
- Performance:
|
|
153
|
+
- Avoid N+1 (`includes`, `preload`).
|
|
154
|
+
- Use DB constraints/indexes for new query patterns.
|
|
155
|
+
- Batch processing for large datasets (`find_each`, `insert_all`).
|
|
156
|
+
- Dependencies:
|
|
157
|
+
- Do not add new dependencies for trivial tasks.
|
|
158
|
+
- Prefer stdlib / Rails built-ins already in the stack.
|
|
159
|
+
|
|
160
|
+
## UI & Frontend
|
|
161
|
+
|
|
162
|
+
- Tailwind v4 tokens live in `app/assets/tailwind/application.css`; use theme variables and brand classes.
|
|
163
|
+
- Forms baseline: “Labels on left” layout.
|
|
164
|
+
- Stimulus-first:
|
|
165
|
+
- Prefer controllers from `tailwindcss-stimulus-components` and `@stimulus-components/*`.
|
|
166
|
+
- New controllers must be generic, reusable, under `app/javascript/controllers/`, and registered in `controllers/index.js`.
|
|
167
|
+
- Copy must follow `docs/brand.md`. Marketing pages must follow `docs/marketing/style-guide.md`.
|
|
168
|
+
- No inline styles (`style="..."`). Use Tailwind utilities.
|
|
169
|
+
- Prefer semantic HTML and accessibility basics (labels, autocomplete where appropriate).
|
|
170
|
+
|
|
171
|
+
## Mailers
|
|
172
|
+
|
|
173
|
+
- Mailer views live under `app/views/mailers/`.
|
|
174
|
+
- Shared layout: `app/views/layouts/mailer.html.erb`
|
|
175
|
+
- Prefer partials under `app/views/mailers/` for shared sections.
|
|
176
|
+
- Use `premailer-rails` to inline styles.
|
|
177
|
+
- Set `deliver_later_queue_name` to `:latency_5m` in mailers.
|
|
178
|
+
|
|
179
|
+
## Testing (must follow)
|
|
180
|
+
|
|
181
|
+
### What tests should do
|
|
182
|
+
|
|
183
|
+
- Tests must cover application behavior, not framework behavior.
|
|
184
|
+
- Tests must validate observable outcomes (rendered content, redirects, DB changes, enqueued jobs, outbound payloads).
|
|
185
|
+
- Avoid:
|
|
186
|
+
- tautological tests (re-implementing method logic in the test)
|
|
187
|
+
- “Rails works” tests
|
|
188
|
+
- status-only assertions with no behavioral check
|
|
189
|
+
- heavy stubbing that can’t catch regressions
|
|
190
|
+
|
|
191
|
+
### Practical rules
|
|
192
|
+
|
|
193
|
+
- Framework: Minitest. Fixtures only (factories have been removed). Do not add factories.
|
|
194
|
+
- Anatomy: setup (optional); exercise; assertions; cleanup (optional). Leave an empty line between sections.
|
|
195
|
+
- Controller/system tests must assert status/redirect AND content (selectors/text) or side effects.
|
|
196
|
+
- System tests: use `data-test-id` selectors; add `data-test-id` attributes in views when needed.
|
|
197
|
+
- Keep review pages small in tests (~10) for speed and deterministic assertions.
|
|
198
|
+
|
|
199
|
+
### HTTP / VCR / webhooks
|
|
200
|
+
|
|
201
|
+
- WebMock enabled.
|
|
202
|
+
- Outgoing API calls: recorded with VCR (see Project map for cassette location).
|
|
203
|
+
- VCR structure mirrors provider/API path segments.
|
|
204
|
+
- Filename: `{platform}_{resource}[optional_suffix].yml`.
|
|
205
|
+
- Incoming webhooks: do not VCR. Use JSON captures (see Project map).
|
|
206
|
+
|
|
207
|
+
Canonical pattern (webhook fixture ingestion):
|
|
208
|
+
|
|
209
|
+
```ruby
|
|
210
|
+
test "DataForSEO resolve with async mode" do
|
|
211
|
+
VCR.use_cassette("dataforseo/serp_google_maps_task_post/google_maps_page_resolve") do
|
|
212
|
+
result = Providers::Dataforseo::GoogleMaps::Page.new(
|
|
213
|
+
mode: :async,
|
|
214
|
+
recording: VCR.current_cassette.recording?,
|
|
215
|
+
platform: :google_maps
|
|
216
|
+
).submit(
|
|
217
|
+
place_eid: @page.place_eid,
|
|
218
|
+
business_name: @location.name,
|
|
219
|
+
country: "GB"
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
assert_equal :scheduled, result.status
|
|
223
|
+
|
|
224
|
+
ExternalTask.create!(
|
|
225
|
+
record_uuid: @page.uuid,
|
|
226
|
+
record_type: @page.class.name,
|
|
227
|
+
provider: Providers::External::DATAFORSEO,
|
|
228
|
+
resource: Providers::Resources::PAGE,
|
|
229
|
+
eid: result.eid
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
payload = WebhookCapture.read(
|
|
233
|
+
provider: Providers::External::DATAFORSEO,
|
|
234
|
+
platform: :google_maps,
|
|
235
|
+
resource: Providers::Resources::PAGE,
|
|
236
|
+
task_eid: result.eid
|
|
237
|
+
)
|
|
238
|
+
assert_not_nil payload
|
|
239
|
+
|
|
240
|
+
response = Providers::Dataforseo::GoogleMaps::Page.new(
|
|
241
|
+
mode: :async,
|
|
242
|
+
platform: :google_maps
|
|
243
|
+
).ingest(payload)
|
|
244
|
+
|
|
245
|
+
assert_equal :ok, response.status
|
|
246
|
+
assert_equal "ChIJrVtdwkDzdkgRHgNW25ELRtQ", response.data.place_eid
|
|
247
|
+
end
|
|
248
|
+
end
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2026-02-08
|
|
9
|
+
|
|
10
|
+
### <!-- 0 -->🚀 Features
|
|
11
|
+
- initialize gem structure with gemspec
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### <!-- 1 -->🐛 Bug Fixes
|
|
15
|
+
- add missing yaml require and improve setup UX
|
|
16
|
+
- hide negative flags in Thor CLI and add local install docs
|
|
17
|
+
- fix git-cliff configuration
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### <!-- 3 -->📚 Documentation
|
|
21
|
+
- fix README URLs and expand About section
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### <!-- 7 -->⚙️ Miscellaneous Tasks
|
|
25
|
+
- update gemspec and README with proper metadata
|
|
26
|
+
- streamline release prep workflow
|
|
27
|
+
- add git hooks and ignore build artifacts
|
|
28
|
+
- update CI ruby matrix and gitignore
|
|
29
|
+
- update CI ruby matrix and gitignore
|
|
30
|
+
|
|
31
|
+
|
data/Gemfile
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
source "https://rubygems.org"
|
|
4
|
+
|
|
5
|
+
# Specify your gem's dependencies in git-markdown.gemspec
|
|
6
|
+
gemspec
|
|
7
|
+
|
|
8
|
+
gem "gem-release", "~> 2.2"
|
|
9
|
+
|
|
10
|
+
group :development do
|
|
11
|
+
gem "commitlint", require: false
|
|
12
|
+
gem "lefthook", require: false
|
|
13
|
+
end
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# git-markdown
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/rb/git-markdown)
|
|
4
|
+
[](https://github.com/ethos-link/git-markdown/actions/workflows/ruby.yml)
|
|
5
|
+
|
|
6
|
+
Convert GitHub pull requests into a single Markdown file you can review locally. Useful for offline or local AI review workflows with tools like [opencode](https://opencode.ai), GitHub Copilot CLI, and your own scripts.
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- 🔐 Zero-config auth: uses your existing GitHub credentials
|
|
11
|
+
- 🧾 Clean output: PR details, threads, and summaries in readable Markdown
|
|
12
|
+
- 🧰 Practical controls: filter by status, write to file or stdout, debug when needed
|
|
13
|
+
- 🏢 GitHub Enterprise support, plus safe local credential storage (XDG-style, tight perms)
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
gem install git-markdown
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick start
|
|
22
|
+
|
|
23
|
+
### 1) Setup (optional)
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
git-markdown setup
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### 2) Export a PR
|
|
30
|
+
|
|
31
|
+
From inside a git repository:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
git-markdown pr 123
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Or specify the repo:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
git-markdown pr owner/repo#123
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
It saves `PR-{number}-{title}.md` in the current directory by default.
|
|
44
|
+
|
|
45
|
+
## Usage
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Export PR from current repository
|
|
49
|
+
git-markdown pr 123
|
|
50
|
+
|
|
51
|
+
# Export PR from a specific repository
|
|
52
|
+
git-markdown pr owner/repo#123
|
|
53
|
+
|
|
54
|
+
# Output to stdout (useful for piping)
|
|
55
|
+
git-markdown pr 123 --stdout | pbcopy # macOS; on Linux use `xclip`/`wl-copy` or redirect to a file
|
|
56
|
+
|
|
57
|
+
# Save to a directory
|
|
58
|
+
git-markdown pr 123 --output ./reviews/
|
|
59
|
+
|
|
60
|
+
# Filter comment threads (default: unresolved)
|
|
61
|
+
git-markdown pr 123 --status=unresolved
|
|
62
|
+
git-markdown pr 123 --status=resolved
|
|
63
|
+
git-markdown pr 123 --status=all
|
|
64
|
+
|
|
65
|
+
# Debug output
|
|
66
|
+
git-markdown pr 123 --debug
|
|
67
|
+
|
|
68
|
+
# Show version
|
|
69
|
+
git-markdown version
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Authentication
|
|
73
|
+
|
|
74
|
+
`git-markdown` looks for a GitHub token in this order:
|
|
75
|
+
|
|
76
|
+
1. `GITHUB_TOKEN` or `GH_TOKEN` environment variables
|
|
77
|
+
2. Git credential store (`git credential fill`)
|
|
78
|
+
3. GitHub CLI (`gh auth token`)
|
|
79
|
+
4. Token saved by `git-markdown setup`
|
|
80
|
+
|
|
81
|
+
If you see a prompt for a GitHub username or an askpass error, set `GITHUB_TOKEN` or `GH_TOKEN` in your environment to skip git credential prompts.
|
|
82
|
+
|
|
83
|
+
## GitHub Enterprise
|
|
84
|
+
|
|
85
|
+
Set your API URL:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
export GITHUB_API_URL=https://github.yourcompany.com/api/v3
|
|
89
|
+
git-markdown pr owner/repo#123
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Output
|
|
93
|
+
|
|
94
|
+
The generated Markdown includes:
|
|
95
|
+
|
|
96
|
+
- PR metadata (title, author, status, created date)
|
|
97
|
+
- PR description (full body)
|
|
98
|
+
- Review comments grouped by file with line numbers
|
|
99
|
+
- General discussion comments
|
|
100
|
+
- Review summaries (approvals, change requests)
|
|
101
|
+
|
|
102
|
+
Threads marked with `[resolved]` or `[done]` are filtered out by default. Use `--status=all` to include them.
|
|
103
|
+
|
|
104
|
+
## Configuration
|
|
105
|
+
|
|
106
|
+
Config is stored under `~/.config/git-markdown/`:
|
|
107
|
+
|
|
108
|
+
- `config.yml` for settings (provider, API URL, defaults)
|
|
109
|
+
- `credentials` for the token (permissions: 0600)
|
|
110
|
+
|
|
111
|
+
## Requirements
|
|
112
|
+
|
|
113
|
+
- Ruby 3.0+
|
|
114
|
+
- Git (for remote detection)
|
|
115
|
+
- GitHub Personal Access Token with `repo` scope
|
|
116
|
+
|
|
117
|
+
## Development
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
git clone https://github.com/ethos-link/git-markdown.git
|
|
121
|
+
cd git-markdown
|
|
122
|
+
|
|
123
|
+
bundle install
|
|
124
|
+
bundle exec rake test
|
|
125
|
+
bundle exec standardrb
|
|
126
|
+
bundle exec rake
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Note: `Gemfile.lock` is intentionally not tracked to avoid conflicts across Ruby versions.
|
|
130
|
+
|
|
131
|
+
### Git hooks
|
|
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. CI also validates commit messages on pull requests and pushes to main/master.
|
|
134
|
+
|
|
135
|
+
Run the hook installer once per clone:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
bundle exec lefthook install
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Install locally
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
rake install
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Release
|
|
148
|
+
|
|
149
|
+
Releases are triggered by pushed tags and use `CHANGELOG.md` for GitHub release notes.
|
|
150
|
+
|
|
151
|
+
If you want changelog automation, install `git-cliff` (<https://github.com/orhun/git-cliff>) locally and use it to update `CHANGELOG.md`.
|
|
152
|
+
|
|
153
|
+
The release workflow expects a `## [X.Y.Z]` entry in `CHANGELOG.md` that matches the tag. Note: `release:prep` enforces a clean working tree and must run on the `main` or `master` branch; it will skip if the changelog has no changes.
|
|
154
|
+
|
|
155
|
+
Before bumping, install dependencies:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
bundle install
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Then run:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
# 1) Bump the version (commit created)
|
|
165
|
+
bundle exec gem bump -v X.Y.Z
|
|
166
|
+
|
|
167
|
+
# 2) Prepare release (changelog + tag + push)
|
|
168
|
+
bundle exec rake release:prep
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Contributing
|
|
172
|
+
|
|
173
|
+
1. Fork it
|
|
174
|
+
2. Create a branch (`git checkout -b feature/my-feature`)
|
|
175
|
+
3. Commit your changes
|
|
176
|
+
4. Push (`git push origin feature/my-feature`)
|
|
177
|
+
5. Open a Pull Request
|
|
178
|
+
|
|
179
|
+
Please use [Conventional Commits](https://www.conventionalcommits.org/) for commit messages.
|
|
180
|
+
|
|
181
|
+
## Roadmap
|
|
182
|
+
|
|
183
|
+
- [ ] GitLab Merge Request support (`git-markdown mr`)
|
|
184
|
+
- [ ] GitLab and GitHub Issue support (`git-markdown issue`)
|
|
185
|
+
- [ ] Custom templates
|
|
186
|
+
- [ ] Optional diff in output
|
|
187
|
+
|
|
188
|
+
## References
|
|
189
|
+
|
|
190
|
+
- GitHub REST API docs: [docs.github.com/en/rest](https://docs.github.com/en/rest)
|
|
191
|
+
- GitHub CLI auth token: [cli.github.com/manual/gh_auth_token](https://cli.github.com/manual/gh_auth_token)
|
|
192
|
+
- Git credential interface: [git-scm.com/docs/git-credential](https://git-scm.com/docs/git-credential)
|
|
193
|
+
- XDG Base Directory spec: [specifications.freedesktop.org/basedir-spec](https://specifications.freedesktop.org/basedir-spec/latest/)
|
|
194
|
+
- RubyGems: [rubygems.org/gems/git-markdown](https://rubygems.org/)
|
|
195
|
+
|
|
196
|
+
## License
|
|
197
|
+
|
|
198
|
+
MIT License, see [LICENSE.txt](LICENSE.txt)
|
|
199
|
+
|
|
200
|
+
## About
|
|
201
|
+
|
|
202
|
+
Made by the team at [Ethos Link](https://www.ethos-link.com) — practical software for growing businesses. We build tools for hospitality and retail teams who don’t have time for complicated setups: plain-language insights, fast onboarding, and real human support so you can get clear insights, take action, and move on.
|
|
203
|
+
|
|
204
|
+
We also build [Reviato](https://www.reviato.com), “More stars. Less hassle.”.
|
|
205
|
+
Reviato helps restaurants, hotels, clinics, vets and other local businesses collect and analyse customer reviews, highlights recurring themes, and turns feedback into actionable next steps.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
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
|
+
require "bundler/gem_tasks"
|
|
6
|
+
require "rake/testtask"
|
|
7
|
+
require "standard/rake"
|
|
8
|
+
require_relative "lib/git/markdown/version"
|
|
9
|
+
|
|
10
|
+
Rake::TestTask.new(:test) do |t|
|
|
11
|
+
t.libs << "test"
|
|
12
|
+
t.libs << "lib"
|
|
13
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
task default: %i[test standard]
|
|
17
|
+
|
|
18
|
+
namespace :release do
|
|
19
|
+
desc "Update changelog, commit, and tag"
|
|
20
|
+
task :prep do
|
|
21
|
+
version = GitMarkdown::VERSION
|
|
22
|
+
branch = `git rev-parse --abbrev-ref HEAD`.strip
|
|
23
|
+
|
|
24
|
+
if branch == "HEAD"
|
|
25
|
+
abort "Release prep requires a branch (not detached HEAD)."
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
unless ["main", "master"].include?(branch)
|
|
29
|
+
abort "Release prep must run on main or master. Current: #{branch}."
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
unless system("git diff --quiet") && system("git diff --cached --quiet")
|
|
33
|
+
abort "Release prep requires a clean working tree."
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
sh "git cliff -c cliff.toml --unreleased --tag v#{version} -o CHANGELOG.md"
|
|
37
|
+
if system("git diff --quiet -- CHANGELOG.md")
|
|
38
|
+
puts "No changelog changes. Skipping release prep."
|
|
39
|
+
next
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
sh "git add CHANGELOG.md"
|
|
43
|
+
sh "git commit -m \"docs: update changelog for v#{version}\""
|
|
44
|
+
sh "bundle exec gem tag -v #{version}"
|
|
45
|
+
sh "git push"
|
|
46
|
+
sh "git push origin v#{version}"
|
|
47
|
+
end
|
|
48
|
+
end
|
data/bin/git-markdown
ADDED
data/cliff.toml
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
[changelog]
|
|
2
|
+
header = "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n"
|
|
3
|
+
body = "{% if version %}## [{{ version | trim_start_matches(pat=\"v\") }}] - {{ timestamp | date(format=\"%Y-%m-%d\") }}\n{% endif %}\n{% for group, commits in commits | group_by(attribute=\"group\") %}{% if commits | length > 0 %}### {{ group }}\n{% for commit in commits %}- {{ commit.message | trim }}\n{% endfor %}\n\n{% endif %}{% endfor %}"
|
|
4
|
+
trim = true
|
|
5
|
+
|
|
6
|
+
[git]
|
|
7
|
+
conventional_commits = true
|
|
8
|
+
filter_unconventional = true
|
|
9
|
+
tag_pattern = "v[0-9]*"
|
|
10
|
+
|
|
11
|
+
[[commit_parsers]]
|
|
12
|
+
message = '^feat(\(.*\))?:'
|
|
13
|
+
group = "Added"
|
|
14
|
+
|
|
15
|
+
[[commit_parsers]]
|
|
16
|
+
message = '^fix(\(.*\))?:'
|
|
17
|
+
group = "Fixed"
|
|
18
|
+
|
|
19
|
+
[[commit_parsers]]
|
|
20
|
+
message = '^docs(\(.*\))?:'
|
|
21
|
+
group = "Documentation"
|
|
22
|
+
|
|
23
|
+
[[commit_parsers]]
|
|
24
|
+
message = '^refactor(\(.*\))?:'
|
|
25
|
+
group = "Changed"
|
|
26
|
+
|
|
27
|
+
[[commit_parsers]]
|
|
28
|
+
message = '^perf(\(.*\))?:'
|
|
29
|
+
group = "Changed"
|
|
30
|
+
|
|
31
|
+
[[commit_parsers]]
|
|
32
|
+
message = '^test(\(.*\))?:'
|
|
33
|
+
group = "Changed"
|
|
34
|
+
|
|
35
|
+
[[commit_parsers]]
|
|
36
|
+
message = '^chore(\(.*\))?:'
|
|
37
|
+
group = "Changed"
|
|
38
|
+
|
|
39
|
+
[[commit_parsers]]
|
|
40
|
+
message = '^build(\(.*\))?:'
|
|
41
|
+
group = "Changed"
|
|
42
|
+
|
|
43
|
+
[[commit_parsers]]
|
|
44
|
+
message = '^ci(\(.*\))?:'
|
|
45
|
+
group = "Changed"
|
|
46
|
+
|
|
47
|
+
[[commit_parsers]]
|
|
48
|
+
message = '^style(\(.*\))?:'
|
|
49
|
+
group = "Changed"
|
|
50
|
+
|
|
51
|
+
[[commit_parsers]]
|
|
52
|
+
message = '^revert(\(.*\))?:'
|
|
53
|
+
group = "Changed"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Gem::Specification.new do |spec|
|
|
4
|
+
spec.name = "git-markdown"
|
|
5
|
+
spec.version = "0.1.0"
|
|
6
|
+
spec.authors = ["ethos-link"]
|
|
7
|
+
|
|
8
|
+
spec.summary = "Convert GitHub PRs to Markdown for local AI code review"
|
|
9
|
+
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."
|
|
10
|
+
spec.homepage = "https://github.com/ethos-link/git-markdown"
|
|
11
|
+
spec.license = "MIT"
|
|
12
|
+
spec.required_ruby_version = ">= 3.0.0"
|
|
13
|
+
|
|
14
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
15
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
|
16
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
17
|
+
|
|
18
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
19
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
20
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
spec.bindir = "bin"
|
|
24
|
+
spec.executables = ["git-markdown"]
|
|
25
|
+
spec.require_paths = ["lib"]
|
|
26
|
+
|
|
27
|
+
spec.add_dependency "thor", "~> 1.0"
|
|
28
|
+
|
|
29
|
+
spec.add_development_dependency "minitest", "~> 6.0"
|
|
30
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
|
31
|
+
spec.add_development_dependency "standard", "~> 1.0"
|
|
32
|
+
spec.add_development_dependency "vcr", "~> 6.0"
|
|
33
|
+
spec.add_development_dependency "webmock", "~> 3.0"
|
|
34
|
+
end
|