전체 이슈 가져오는 방법입니다 ☺️
# 0) UTF-8 고정
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()
[Console]::InputEncoding = [System.Text.UTF8Encoding]::new()
$ErrorActionPreference = "Stop"
# 1) 변수
$SRC = "원본 레포"
$DST = "대상 레포"
# 2) 소스 이슈 번호 수집 (PR은 어차피 issue list에 안 섞임)
$srcAll = gh issue list -R $SRC --state all --limit 2000 --json number | ConvertFrom-Json
$issueIds = $srcAll | ForEach-Object { $_.number }
"source issues: $($issueIds.Count) found"
if (-not $issueIds -or $issueIds.Count -eq 0) {
throw "No issues to migrate from $SRC"
}
# 3) 라벨 동기화
$srcLabels = (gh api "repos/$SRC/labels?per_page=100" --paginate) | ConvertFrom-Json
$dstLabels = (gh api "repos/$DST/labels?per_page=100" --paginate) | ConvertFrom-Json
$dstLabelNames = @{}; $dstLabels | % { $dstLabelNames[$_.name] = $true }
foreach ($lab in $srcLabels) {
if (-not $dstLabelNames.ContainsKey($lab.name)) {
gh api -X POST "repos/$DST/labels" -f name="$($lab.name)" -f color="$($lab.color)" | Out-Null
Start-Sleep -Milliseconds 60
}
}
# 4) 마일스톤 동기화(제목 기반)
$srcMsAll = (gh api "repos/$SRC/milestones?state=all&per_page=100" --paginate) | ConvertFrom-Json
foreach ($ms in $srcMsAll) {
$exists = (gh api "repos/$DST/milestones?state=all&per_page=100" --paginate) | ConvertFrom-Json |
Where-Object { $_.title -eq $ms.title } | Select-Object -First 1
if (-not $exists) {
gh api -X POST "repos/$DST/milestones" -f title="$($ms.title)" | Out-Null
Start-Sleep -Milliseconds 60
}
}
$dstMs = (gh api "repos/$DST/milestones?state=all&per_page=100" --paginate) | ConvertFrom-Json
$dstMsByTitle = @{}; $dstMs | % { $dstMsByTitle[$_.title] = $_ }
# 5) 마이그레이션 (번호별 개별 조회 → 생성/패치)
foreach ($n in $issueIds) {
# 중복 방지: 이미 이관된 건 스킵
$marker = "migrated from $SRC#$n"
$dup = gh issue list -R $DST --state all --search "$marker" --json number | ConvertFrom-Json
if ($dup -and $dup.Count -gt 0) { "skip dup -> src #$n"; continue }
# a) 원본 단일 이슈 풀 조회 (필드 Null 안정성 ↑)
$i = gh issue view -R $SRC $n --json `
number,title,body,url,state,labels,assignees,milestone,comments `
| ConvertFrom-Json
if (-not $i) { "skip (cannot view) -> src #$n"; continue }
$origin = "[$marker | $($i.url)]`n`n"
$bodyTxt = if ($null -ne $i.body) { [string]$i.body } else { "" }
$bodyFull = @"
$origin$bodyTxt
"@
# b) 이슈 생성 (필드 방식)
$newNum = gh api -X POST "repos/$DST/issues" `
-f title="$($i.title)" `
-f body="$bodyFull" `
--jq ".number"
if (-not $newNum) { throw "create failed -> src #$($i.number)" }
"created -> dst #$newNum (from src #$($i.number))"
# c) 마일스톤
if ($i.milestone -and $dstMsByTitle.ContainsKey($i.milestone.title)) {
gh api -X PATCH "repos/$DST/issues/$newNum" -f milestone="$($dstMsByTitle[$i.milestone.title].number)" | Out-Null
}
# d) 라벨
foreach ($lab in $i.labels) {
gh api -X POST "repos/$DST/issues/$newNum/labels" -f "labels[]=$($lab.name)" | Out-Null
Start-Sleep -Milliseconds 30
}
# e) 어싸이니
foreach ($a in $i.assignees) {
gh api -X POST "repos/$DST/issues/$newNum/assignees" -f "assignees[]=$($a.login)" | Out-Null
Start-Sleep -Milliseconds 30
}
# f) 코멘트
foreach ($c in $i.comments) {
$cBody = if ($null -ne $c.body) { [string]$c.body } else { "" }
$cFull = @"
[from @$($c.author.login) | $($c.createdAt)]
$cBody
"@
gh api -X POST "repos/$DST/issues/$newNum/comments" -f body="$cFull" | Out-Null
Start-Sleep -Milliseconds 30
}
# g) 상태
if ($i.state -eq "CLOSED") {
gh api -X PATCH "repos/$DST/issues/$newNum" -f state="closed" | Out-Null
}
"ok -> dst #$newNum"
Start-Sleep -Milliseconds 90
}
# 6) 결과 확인
gh issue list -R $DST --state all --limit 20