Last active
October 15, 2023 15:26
-
-
Save EitanBlumin/f4ead30a188e04c806f3f6bebf20fcf0 to your computer and use it in GitHub Desktop.
Import GitHub Issues from Trello Cards using Powershell ( https://eitanblumin.com/2019/05/07/import-github-issues-from-trello-cards-using-powershell/ )
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
param ( | |
[Parameter(Mandatory)][string] $GitHubToken, | |
[Parameter(Mandatory)][string] $GitHubOwner, | |
[Parameter(Mandatory)][string] $GitHubRepo, | |
[Parameter(Mandatory)][string] $SourceTrelloJsonFile, | |
[string[]] $TrelloLists, | |
[bool] $UpdateExistingIssuesByTitle = $true, | |
[bool] $AddNonExistingIssues = $true, | |
[bool] $Logging = $true | |
) | |
$ErrorActionPreference = "Stop" | |
# Function for converting Unicode to Ascii strings | |
function Convert-FromUnicode { | |
Param([string]$a) | |
$b=[system.text.encoding]::UTF8.GetBytes($a) | |
$c=[system.text.encoding]::convert([text.encoding]::UTF8,[text.encoding]::ASCII,$b) | |
-join [system.text.encoding]::ASCII.GetChars($c) | |
} | |
# clear screen for logging | |
if ($Logging) { cls } | |
# init empty issue variable | |
$newIssue = New-Object PSObject | |
$newIssue | add-member Noteproperty title "" | |
$newIssue | add-member Noteproperty body "" | |
$newIssue | add-member Noteproperty labels @("enhancement") | |
# init empty label variable | |
$newLabel = New-Object PSObject | |
$newLabel | add-member Noteproperty name "" | |
$newLabel | add-member Noteproperty description "" | |
$newLabel | add-member Noteproperty color "" | |
# load json file into variable | |
$j = gc $SourceTrelloJsonFile | ConvertFrom-Json | |
# save all non-archived lists in a hash variable | |
$lists = @{} | |
$j.lists | select id, name, closed | where closed -NE "True" | % {$lists.add($_.id, $_.name)} | |
# if $TrelloLists was specified, filter only requested trello lists | |
if ($TrelloLists.Count -gt 0) { | |
# first, trim whitespaces for each item in the parameter | |
for ($i = 0; $i -lt $TrelloLists.Count; $i++) { | |
$TrelloLists[$i] = $TrelloLists[$i].Trim() | |
} | |
# next, remove from $lists items that weren't specified in the parameter | |
# must clone the list first, otherwise we won't be able to modify it while iterating through it | |
$listsClone = $lists.Clone() | |
$listsClone.Keys | foreach { if ($TrelloLists -notcontains $lists[$_]) { $lists.Remove($_) } } | |
} | |
# save all labels with HEX color representation in a hash variable | |
$labels = @{} | |
$j.labels | select name, color | % {$labels.add($_.name, ([Convert]::ToString([System.Drawing.ColorTranslator]::FromHtml($_.color).ToArgb(), 16).ToLower()).Substring(2))} | |
# add lists as labels | |
$lists.Keys | foreach { if ($labels.Keys -notcontains $lists[$_]) { | |
# generate random color for new label and add it | |
$randcolor = Get-Random -Minimum ([System.Drawing.ColorTranslator]::FromHtml("black").ToArgb()) -Maximum ([System.Drawing.ColorTranslator]::FromHtml("white").ToArgb()) | |
$labels.Add($lists[$_], ([Convert]::ToString($randcolor, 16).ToLower()).Substring(2)) | |
} } | |
# get existing labels in GitHub and save in an array | |
$githubUri = "https://api.github.com/repos/$GitHubOwner/$GitHubRepo/labels" + "?access_token=$GitHubToken" | |
$githubResponse = (Invoke-WebRequest -Uri $githubUri -Method GET).Content | ConvertFrom-Json | |
$githubLabels = [string[]]@() | |
$githubResponse | select name | foreach { $githubLabels += $_.name } | |
# add any missing labels to the GitHub repo | |
$labels.Keys | foreach { | |
if ($githubLabels -notcontains $_) { | |
$currLabel = $newLabel | |
$currLabel.name = $_ | |
$currLabel.description = "Label imported from Trello" | |
$currLabel.color = $labels[$_] | |
$currLabel = $currLabel | ConvertTo-Json | |
if ($Logging) { "adding missing label: " + $currLabel } | |
$githubResponse = Invoke-WebRequest -Uri $githubUri -Body $currLabel -Method "POST" | |
#$githubResponse.Content | ConvertFrom-Json | |
} | |
} | |
# if UpdateExistingIssuesByTitle is enabled, get all existing GitHub issues | |
if ($UpdateExistingIssuesByTitle -eq $true) { | |
$page = 1 | |
$githubUri = "https://api.github.com/repos/$GitHubOwner/$GitHubRepo/issues?access_token=$GitHubToken&page=$page" | |
$githubResponse = (Invoke-WebRequest -Uri $githubUri -Method GET).Content | ConvertFrom-Json | |
$existingIssues = @() | |
while ($githubResponse.Count -gt 0) { | |
if ($Logging) {"loaded github issues page $page : " + $githubResponse.Count + " issue(s)"} | |
$existingIssues += $githubResponse | foreach {$_} | select url, title, state, body, labels | where state -EQ "open" | |
$page += 1 | |
$githubUri = "https://api.github.com/repos/$GitHubOwner/$GitHubRepo/issues?access_token=$GitHubToken&page=$page" | |
$githubResponse = (Invoke-WebRequest -Uri $githubUri -Method GET).Content | ConvertFrom-Json | |
} | |
if ($Logging) {"found " + $existingIssues.Count + " issues total `n"} | |
} | |
# process per list | |
foreach ($listid in $lists.Keys) { | |
if ($Logging) {" `nProcessing List: " + $lists[$listid]} | |
# inspect cards for current list | |
foreach ($card in ($j.cards | select name, desc, labels, idList, id, shortUrl, attachments, closed, @{name="list"; expression={$lists[$_.idList]}} | where idList -eq $listid | where closed -NE "True")) { | |
# init empty variable for issue | |
$issue = $null | |
# if updating of existing issues is enabled | |
if ($UpdateExistingIssuesByTitle -eq $true) | |
{ | |
# check if issue already exists based on title | |
$issue = $existingIssues | where title -eq (Convert-FromUnicode $card.name) | |
if ($issue) { | |
if (($issue.GetType()).Name -eq "Object[]") { $issue = $issue[0] } | |
$githubUri = $issue.url + "?access_token=$GitHubToken" | |
$issue.PSObject.Properties.Remove('url') | |
$method = "PATCH" | |
} | |
} | |
# if no existing issue was found, and adding of new issues is enabled, init variables accordingly | |
if ($issue -eq $null -and $AddNonExistingIssues -eq $true) { | |
$githubUri = "https://api.github.com/repos/$GitHubOwner/$GitHubRepo/issues?access_token=$GitHubToken" | |
$issue = $newIssue | |
$issue.title = $card.name | |
$method = "POST" | |
} | |
if ($issue -ne $null) { | |
# init issue new body | |
$issue.body = $card.desc | |
# save checklists as checklists in body | |
$j.checklists | select id, name, idCard, checkItems | where idCard -EQ $card.id | foreach { | |
$issue.body += " `n### " + $_.name | |
foreach ($currCheckItem in $_.checkItems) { | |
$issue.body += " `n" | |
if ($currCheckItem.state -eq "incomplete") { | |
$issue.body += "- [ ] " | |
} else { | |
$issue.body += "- [X] " | |
} | |
$issue.body += $currCheckItem.name | |
} | |
} | |
# save attachments as unordered list of links in body | |
if ($card.attachments.Count -ge 1) { | |
$issue.body += " `n `n## Attachments `n" | |
} | |
$card.attachments | select name, url | foreach { | |
$issue.body += " `n- [" + $_.name + "](" + $_.url + ")" | |
} | |
# save original Trello source URL in body | |
$issue.body += " `n `n> Trello Source: " + $card.shortUrl | |
# add labels | |
$issue.labels = @($lists[$listid]) | |
$card.labels | foreach { $issue.labels += $_.name } | |
$issue.labels = @() + ($issue.labels | select -uniq) # adding @() ensures that single items are still formatted as array | |
# convert issue object to JSON and send to GitHub | |
$newIssueBody = $issue | ConvertTo-Json | |
$newIssueBody = Convert-FromUnicode $newIssueBody | |
$githubResponse = Invoke-WebRequest -Uri $githubUri -Body $newIssueBody -Method $method | |
$issue = $githubResponse.Content | ConvertFrom-Json | |
if ($Logging) {$method + " " + $issue.title + ": " + $issue.url} | |
} else { | |
if ($Logging) {"SKIP " + $card.name} | |
} | |
} | |
} |
% : Unable to find type [System.Drawing.ColorTranslator].
At C:\Users\User\Desktop\trello_cards_to_github_issues.ps1:60 char:34
- ... me, color | % {$labels.add($_.name, ([Convert]::ToString([System.Draw ...
-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- CategoryInfo : InvalidOperation: (System.Drawing.ColorTranslator:TypeName) [ForEach-Object], RuntimeException
- FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.ForEachObjectCommand
ForEach-Object: /Users/userid/trello_cards_to_github_issues.ps1:60
Line |
60 | … me, color | % {$labels.add($_.name, ([Convert]::ToString([System.Draw …
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Exception calling "Add" with "2" argument(s): "Item has already been added. Key in dictionary: '' Key being added: ''"
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
$AddNonExistingIssues doesn't work right if not combined with $UpdateExistingIssuesByTitle
Need to fix