-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add guess-milestone-for-issue custom command
- Loading branch information
Showing
1 changed file
with
105 additions
and
80 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,80 +1,105 @@ | ||
#!/usr/bin/env nu | ||
# Author: hustcer | ||
# Created: 2024/06/09 20:33:15 | ||
# TODO: | ||
# [x] Support Windows, macOS, Linux | ||
# [x] Should run on local machine or Github runners | ||
# Description: Scripts for Github milestone management. | ||
|
||
use common.nu [ECODE, hr-line is-installed] | ||
|
||
export-env { | ||
$env.config.table.mode = 'light' | ||
$env.config.color_config.leading_trailing_space_bg = { attr: n } | ||
} | ||
|
||
export def 'milestone-update' [ | ||
repo: string, # Github repository name | ||
--gh-token(-t): string, # Github access token | ||
--milestone(-m): string, # Milestone name | ||
--pr: string, # The PR number/url/branch of the PR that we want to add milestone. | ||
--force(-f), # Force update milestone even if the milestone is already set. | ||
--dry-run(-d), # Dry run, only print the milestone that would be set. | ||
] { | ||
if not (is-installed 'gh') { | ||
print 'gh command not found, please install it first, see: https://cli.github.com/.' | ||
exit $ECODE.MISSING_BINARY | ||
} | ||
if ($gh_token | is-not-empty) { $env.GH_TOKEN = $gh_token } | ||
let IGNORED_PR_STATUS = [CLOSED OPEN] | ||
# Could be MERGED, OPEN, CLOSED. | ||
let prState = gh pr view $pr --repo $repo --json 'state' | from json | get state | ||
if ($prState in $IGNORED_PR_STATUS) { | ||
print $'PR (ansi p)($pr)(ansi reset) is in state (ansi p)($prState)(ansi reset), will be ignored.' | ||
return | ||
} | ||
let selected = if ($milestone | is-empty) { guess-milestone $repo $pr } else { $milestone } | ||
if $force { | ||
let prevMilestone = gh pr view $pr --repo $repo --json 'milestone' | from json | get milestone?.title? | default '-' | ||
let shouldRemove = $prevMilestone != $selected | ||
if $dry_run and $shouldRemove { | ||
print $'(char nl)Would remove milestone for PR (ansi p)($pr)(ansi reset) in repository (ansi p)($repo)(ansi reset) ...' | ||
} else if $shouldRemove { | ||
gh pr edit $pr --repo $repo --remove-milestone | ||
} else { | ||
print $'(char nl)Milestone for PR (ansi p)($pr)(ansi reset) in repo (ansi p)($repo)(ansi reset) was already set to (ansi p)($prevMilestone)(ansi reset), will be ignored.' | ||
} | ||
} | ||
print $'(char nl)Setting milestone to (ansi p)($selected)(ansi reset) for PR (ansi p)($pr)(ansi reset) in repository (ansi p)($repo)(ansi reset) ...' | ||
# FIXME: GraphQL: Resource not accessible by integration (updatePullRequest) | ||
if not $dry_run { | ||
gh pr edit $pr --repo $repo --milestone $selected | ||
} | ||
} | ||
|
||
# Guess milestone by the merged date of the PR and the infomation of open milestones. | ||
def guess-milestone [repo: string, pr: string] { | ||
# Query github open milestone list by gh | ||
let milestones = gh api -X GET $'/repos/($repo)/milestones' --paginate | from json | ||
| select number title due_on html_url | ||
if ($milestones | is-empty) { | ||
print 'No open milestones found.' | ||
exit $ECODE.CONDITION_NOT_SATISFIED | ||
} | ||
print 'Open milestones:'; hr-line | ||
$milestones | table -w 120 | print | ||
let milestones = $milestones | upsert due_on {|it| | ||
# + 1day to avoid the case that the PR is merged on the due date of the milestone. | ||
if ($it.due_on | is-empty) { (date now) - 1day } else { ($it.due_on | into datetime) + 1day } | ||
} | ||
let mergedAt = gh pr view $pr --repo $repo --json 'mergedAt' | ||
| from json | get mergedAt | into datetime | ||
let guess = $milestones | where due_on >= $mergedAt | sort-by due_on | ||
let milestone = if ($guess | is-empty) { | ||
print 'No milestone found due after the PR merged. Fall back to the latest milestone.' | ||
$milestones | sort-by due_on | first | ||
} else { $guess | first } | ||
$milestone.title | ||
} | ||
|
||
alias main = milestone-update | ||
#!/usr/bin/env nu | ||
# Author: hustcer | ||
# Created: 2024/06/09 20:33:15 | ||
# TODO: | ||
# [x] Support Windows, macOS, Linux | ||
# [x] Should run on local machine or Github runners | ||
# Description: Scripts for Github milestone management. | ||
|
||
use common.nu [ECODE, hr-line is-installed] | ||
|
||
export-env { | ||
$env.config.table.mode = 'light' | ||
$env.config.color_config.leading_trailing_space_bg = { attr: n } | ||
} | ||
|
||
export def 'milestone-update' [ | ||
repo: string, # Github repository name | ||
--gh-token(-t): string, # Github access token | ||
--milestone(-m): string, # Milestone name | ||
--pr: string, # The PR number/url/branch of the PR that we want to add milestone. | ||
--force(-f), # Force update milestone even if the milestone is already set. | ||
--dry-run(-d), # Dry run, only print the milestone that would be set. | ||
] { | ||
if not (is-installed 'gh') { | ||
print 'gh command not found, please install it first, see: https://cli.github.com/.' | ||
exit $ECODE.MISSING_BINARY | ||
} | ||
if ($gh_token | is-not-empty) { $env.GH_TOKEN = $gh_token } | ||
let IGNORED_PR_STATUS = [CLOSED OPEN] | ||
# Could be MERGED, OPEN, CLOSED. | ||
let prState = gh pr view $pr --repo $repo --json 'state' | from json | get state | ||
if ($prState in $IGNORED_PR_STATUS) { | ||
print $'PR (ansi p)($pr)(ansi reset) is in state (ansi p)($prState)(ansi reset), will be ignored.' | ||
return | ||
} | ||
let selected = if ($milestone | is-empty) { guess-milestone $repo $pr } else { $milestone } | ||
if $force { | ||
let prevMilestone = gh pr view $pr --repo $repo --json 'milestone' | from json | get milestone?.title? | default '-' | ||
let shouldRemove = $prevMilestone != $selected | ||
if $dry_run and $shouldRemove { | ||
print $'(char nl)Would remove milestone for PR (ansi p)($pr)(ansi reset) in repository (ansi p)($repo)(ansi reset) ...' | ||
} else if $shouldRemove { | ||
gh pr edit $pr --repo $repo --remove-milestone | ||
} else { | ||
print $'(char nl)Milestone for PR (ansi p)($pr)(ansi reset) in repo (ansi p)($repo)(ansi reset) was already set to (ansi p)($prevMilestone)(ansi reset), will be ignored.' | ||
} | ||
} | ||
print $'(char nl)Setting milestone to (ansi p)($selected)(ansi reset) for PR (ansi p)($pr)(ansi reset) in repository (ansi p)($repo)(ansi reset) ...' | ||
# FIXME: GraphQL: Resource not accessible by integration (updatePullRequest) | ||
if not $dry_run { | ||
gh pr edit $pr --repo $repo --milestone $selected | ||
} | ||
} | ||
|
||
# Guess milestone by the merged date of the PR and the infomation of open milestones. | ||
def guess-milestone [repo: string, pr: string] { | ||
# Query github open milestone list by gh | ||
let milestones = gh api -X GET $'/repos/($repo)/milestones' --paginate | from json | ||
| select number title due_on html_url | ||
if ($milestones | is-empty) { | ||
print 'No open milestones found.' | ||
exit $ECODE.CONDITION_NOT_SATISFIED | ||
} | ||
print 'Open milestones:'; hr-line | ||
$milestones | table -w 120 | print | ||
let milestones = $milestones | upsert due_on {|it| | ||
# + 1day to avoid the case that the PR is merged on the due date of the milestone. | ||
if ($it.due_on | is-empty) { (date now) - 1day } else { ($it.due_on | into datetime) + 1day } | ||
} | ||
let mergedAt = gh pr view $pr --repo $repo --json 'mergedAt' | ||
| from json | get mergedAt | into datetime | ||
let guess = $milestones | where due_on >= $mergedAt | sort-by due_on | ||
let milestone = if ($guess | is-empty) { | ||
print 'No milestone found due after the PR merged. Fall back to the latest milestone.' | ||
$milestones | sort-by due_on | first | ||
} else { $guess | first } | ||
$milestone.title | ||
} | ||
|
||
# Guess milestone for an issue by the commit message of the last closed PR. | ||
export def guess-milestone-for-issue [ | ||
repo: string, # Github repository name | ||
issueNO: string, # Issue number | ||
] { | ||
let pr = gh api $'/repos/($repo)/issues/($issueNO)/timeline' | ||
| from json | ||
| where event == closed | ||
| filter { $in.commit_id | is-not-empty } | ||
| get 0.commit_url | ||
| gh api $in | ||
| from json | ||
| get commit.message | ||
| lines | ||
| first | ||
| parse --regex '\(#(?<pr>\d+)\)' | ||
| get pr.0 | ||
|
||
let milestone = gh pr view --repo $repo $pr --json 'milestone' | ||
| from json | ||
| get milestone?.title? | ||
| default - | ||
{ milestone: $milestone, fixPR: $pr } | ||
} | ||
|
||
alias main = milestone-update |