|
#!/usr/bin/env kotlin |
|
|
|
@file:DependsOn("org.kohsuke:github-api:1.301") |
|
@file:DependsOn("com.github.ajalt.clikt:clikt-jvm:3.4.0") |
|
|
|
import com.github.ajalt.clikt.core.CliktCommand |
|
import com.github.ajalt.clikt.core.subcommands |
|
import com.github.ajalt.clikt.parameters.options.defaultLazy |
|
import com.github.ajalt.clikt.parameters.options.flag |
|
import com.github.ajalt.clikt.parameters.options.multiple |
|
import com.github.ajalt.clikt.parameters.options.option |
|
import com.github.ajalt.clikt.parameters.options.prompt |
|
import org.kohsuke.github.GHIssue |
|
import org.kohsuke.github.GHIssueState |
|
import org.kohsuke.github.GHMilestoneState |
|
import org.kohsuke.github.GHProject |
|
import org.kohsuke.github.GHProjectCard |
|
import org.kohsuke.github.GHProjectColumn |
|
import org.kohsuke.github.GitHub |
|
|
|
CLI().subcommands(Import(), Clear()).main(args) |
|
|
|
class CLI : CliktCommand( |
|
name = "ghImportMilestoneToProject.main.kts", |
|
epilog = "GitHub authentication is handled by https://github-api.kohsuke.org", |
|
printHelpOnEmptyArgs = true |
|
) { |
|
override fun run() {} |
|
} |
|
|
|
abstract class BaseCommand : CliktCommand() { |
|
val dry: Boolean by option(help = "Only report changes and skip writing them").flag() |
|
val repository: String by option(help = "The name of the repository").prompt("Repository") |
|
val milestone: String by option(help = "The name of the milestone to source the issues").prompt("Milestone") |
|
val project: String by option(help = "The name of the project to import the issues").defaultLazy(defaultForHelp = "milestone") { milestone } |
|
} |
|
|
|
class Import : BaseCommand() { |
|
val open: String? by option(help = "The name of the column to import open issues") |
|
val closed: String? by option(help = "The name of the column to import closed issues") |
|
|
|
override fun run() { |
|
if (open == null && closed == null) error("No columns specified") |
|
with(GitHub.connect()) { |
|
val repo = getRepository(repository) |
|
val mil = repo.listMilestones(GHIssueState.ALL).firstOrNull { it.title == milestone } |
|
?: error("Milestone '$milestone' not found") |
|
if (mil.state == GHMilestoneState.CLOSED) error("Milestone '${mil.title}' is closed") |
|
|
|
val proj: GHProject = repo.listProjects(GHProject.ProjectStateFilter.ALL).firstOrNull { it.name == project } |
|
?: error("Project '$project' not found") |
|
if (proj.state == GHProject.ProjectState.CLOSED) { |
|
error("Project '${proj.name}' is closed") |
|
} else { |
|
val columns: Iterable<GHProjectColumn> = proj.listColumns() |
|
val projectIssues by lazy { |
|
columns.flatMap(GHProjectColumn::listCards).map(GHProjectCard::getContent) |
|
} |
|
val projectIssueIds by lazy { |
|
projectIssues.map(GHIssue::getId) |
|
} |
|
|
|
if (open != null) { |
|
val openColumn = columns.firstOrNull { it.name == open } ?: error("Open column '$open' not found") |
|
openColumn.addIssues(projectIssueIds, repo.getIssues(GHIssueState.OPEN, mil)) |
|
} |
|
if (closed != null) { |
|
val closedColumn = columns.firstOrNull { it.name == closed } ?: error("Closed column '$closed' not found") |
|
closedColumn.addIssues(projectIssueIds, repo.getIssues(GHIssueState.CLOSED, mil)) |
|
} |
|
} |
|
} |
|
} |
|
|
|
private fun GHProjectColumn.addIssues(existing: Iterable<Long>, toAdd: Iterable<GHIssue>) { |
|
toAdd.filter { it.id !in existing }.forEach { |
|
if (!dry) { |
|
val card: GHProjectCard = createCard(it) |
|
println("Card created: column=${this.name}, card=${card.htmlUrl}") |
|
} else { |
|
println("Card to create: column=${this.name}, issue=${it.htmlUrl}") |
|
} |
|
} |
|
} |
|
} |
|
|
|
class Clear : BaseCommand() { |
|
val column: List<String> by option(help = "The names of the columns to clear").multiple() |
|
|
|
override fun run() { |
|
with(GitHub.connect()) { |
|
val repo = getRepository(repository) |
|
val mil = repo.listMilestones(GHIssueState.ALL).firstOrNull { it.title == milestone } |
|
?: error("Milestone '$milestone' not found") |
|
if (mil.state == GHMilestoneState.CLOSED) error("Milestone '${mil.title}' is closed") |
|
|
|
val proj: GHProject = repo.listProjects(GHProject.ProjectStateFilter.ALL).firstOrNull { it.name == project } |
|
?: error("Project '$project' not found") |
|
if (proj.state == GHProject.ProjectState.CLOSED) { |
|
error("Project '${proj.name}' is closed") |
|
} else { |
|
val columns: Iterable<GHProjectColumn> = proj.listColumns() |
|
val targetColumns = if (column.isEmpty()) { |
|
columns |
|
} else { |
|
column.map { c -> columns.firstOrNull { it.name == c } ?: error("Column '$c' not found") } |
|
} |
|
targetColumns.forEach { c -> |
|
val cards = c.listCards().filter { |
|
it.content?.milestone?.title == milestone |
|
} |
|
cards.forEach { |
|
if (!dry) { |
|
it.delete() |
|
println("Card removed: column=${c.name}, issue=${it.content.htmlUrl}") |
|
} else { |
|
println("Card to remove: column=${c.name}, issue=${it.content.htmlUrl}") |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |