forked from Gitlink/gitlink-cli
feat(pr): surface merged_at in pr +view
Signed-off-by: 林晨 (Leo Cheng) <chengkelfan@qq.com>
This commit is contained in:
parent
9749a4c832
commit
c4a0ac1f13
|
|
@ -0,0 +1,28 @@
|
||||||
|
# PR View Merged Timestamp
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
`pr +view` now surfaces the merge timestamp at the top level of its output.
|
||||||
|
|
||||||
|
The non-v1 detail endpoint `/{owner}/{repo}/pulls/{id}` nests the merge time under
|
||||||
|
`pull_request.merged_at` (an ISO-8601 string such as `2026-07-05T12:52:05+08:00`),
|
||||||
|
but the CLI previously only lifted `closed_at`. Merged PRs therefore showed no merge
|
||||||
|
time, mirroring upstream issue #14.
|
||||||
|
|
||||||
|
The `closed_at` enrichment is renamed to `enrichPullRequestTimestamps` and extended so
|
||||||
|
that, when `pull_request.merged_at` is present, it is copied to `merged_at` at the top
|
||||||
|
level (and the boolean `merged`, when present, is surfaced alongside it). This matches
|
||||||
|
`gh pr view`, which exposes `mergedAt`. The existing `closed_at` behavior is unchanged.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gitlink-cli pr +view --owner Gitlink --repo forgeplus --id 42
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"merged_at": "2026-07-05T12:52:05+08:00",
|
||||||
|
"merged": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
@ -138,7 +138,7 @@ func Shortcuts(translators ...*i18n.Translator) []*common.Shortcut {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := enrichPullRequestClosedAt(ctx, env); err != nil {
|
if err := enrichPullRequestTimestamps(ctx, env); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return ctx.Output(env)
|
return ctx.Output(env)
|
||||||
|
|
@ -470,13 +470,22 @@ func extractIssueID(env *output.Envelope) (int64, error) {
|
||||||
return int64(idFloat), nil
|
return int64(idFloat), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func enrichPullRequestClosedAt(ctx *common.RuntimeContext, env *output.Envelope) error {
|
func enrichPullRequestTimestamps(ctx *common.RuntimeContext, env *output.Envelope) error {
|
||||||
data, ok := env.Data.(map[string]interface{})
|
data, ok := env.Data.(map[string]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
pr, ok := data["pull_request"].(map[string]interface{})
|
pr, ok := data["pull_request"].(map[string]interface{})
|
||||||
if !ok || !isClosedPullRequest(pr) || stringField(pr, "closed_at") != "" {
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if mergedAt := stringField(pr, "merged_at"); mergedAt != "" {
|
||||||
|
data["merged_at"] = mergedAt
|
||||||
|
if merged, ok := pr["merged"].(bool); ok {
|
||||||
|
data["merged"] = merged
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !isClosedPullRequest(pr) || stringField(pr, "closed_at") != "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
issue, ok := data["issue"].(map[string]interface{})
|
issue, ok := data["issue"].(map[string]interface{})
|
||||||
|
|
|
||||||
|
|
@ -249,6 +249,44 @@ func TestPRView(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPRViewSurfacesMergedAt(t *testing.T) {
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.URL.Path != "/owner/repo/pulls/42.json" {
|
||||||
|
t.Fatalf("unexpected path: %s", r.URL.Path)
|
||||||
|
}
|
||||||
|
writeJSON(t, w, map[string]interface{}{
|
||||||
|
"id": float64(42),
|
||||||
|
"pull_request": map[string]interface{}{
|
||||||
|
"merged_at": "2026-07-05T12:52:05+08:00",
|
||||||
|
"merged": true,
|
||||||
|
"pull_request_staus": "merged",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
ctx := &common.RuntimeContext{
|
||||||
|
Client: &client.Client{HTTP: server.Client(), BaseURL: server.URL},
|
||||||
|
Owner: "owner",
|
||||||
|
Repo: "repo",
|
||||||
|
Format: "json",
|
||||||
|
}
|
||||||
|
env, err := ctx.CallAPI("GET", "/owner/repo/pulls/42", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("CallAPI error: %v", err)
|
||||||
|
}
|
||||||
|
if err := enrichPullRequestTimestamps(ctx, env); err != nil {
|
||||||
|
t.Fatalf("enrich error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data, ok := env.Data.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("unexpected data type: %T", env.Data)
|
||||||
|
}
|
||||||
|
assertEqual(t, data["merged_at"], "2026-07-05T12:52:05+08:00")
|
||||||
|
assertEqual(t, data["merged"], true)
|
||||||
|
}
|
||||||
|
|
||||||
// --- merge ---
|
// --- merge ---
|
||||||
|
|
||||||
func TestPRMerge(t *testing.T) {
|
func TestPRMerge(t *testing.T) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue