Most of my new CPAN modules use Github for issue tracking because of the nice integration with pull requests. I recently wanted to migrate an older distribution to using Github, but didn't want to track tickets in two places.
A while ago, Yanick Champoux wrote Bandying tickets from RT to Github, which looked like exactly what I wanted. Unfortunately, it used the old Github API, which is no longer supported.
I've fixed it up and am sharing it here. If you have your Github user and an OAuth token in your git config file as github.user
and github.token
, it will use those as defaults. Ditto if you have your PAUSE credentials in a .pause file already for uploading.
If you don't have a github OAuth token, get one with this script, which is right out of the Net::Github SYNOPSIS:
#!/usr/bin/env perl use v5.10; use strict; use warnings; use autodie; use Net::GitHub::V3; use IO::Prompt::Tiny qw/prompt/; my $user = prompt( "Github username:" ); my $pass = prompt( "Github password:" ); my $gh = Net::GitHub::V3->new( login => $user, pass => $pass ); my $oauth = $gh->oauth; my $o = $oauth->create_authorization( { scopes => ['user', 'public_repo', 'repo', 'gist'], # just ['public_repo'] note => 'Net::GitHub', } ); say $o->{token};
Here is the script that does the work. There are a bunch of heuristics that are very specific to my way of working (like getting the distribution name out of a dist.ini
file), but those only set prompt defaults so you should be able to use this as is or tweak it to your needs.
#!/usr/bin/env perl use v5.10; use strict; use warnings; use Carp; use IO::Prompt::Tiny qw/prompt/; use Net::GitHub; use Path::Tiny; use RT::Client::REST::Ticket; use RT::Client::REST; use Syntax::Keyword::Junction qw/any/; sub _git_config { my $key = shift; chomp( my $value = `git config --get $key` ); croak "Unknown $key" unless $value; return $value; } my $pause_rc = path( $ENV{HOME}, ".pause" ); my %pause; sub _pause_rc { my $key = shift; if ( $pause_rc->exists && !%pause ) { %pause = split " ", $pause_rc->slurp; } return $pause{$key} // ''; } sub _dist_name { # dzil only for now my $dist = path("dist.ini"); if ( $dist->exists ) { my ($first) = $dist->lines( { count => 1 } ); my ($name) = $first =~ m/name\s*=\s*(\S+)/; return $name if defined $name; } return ''; } my $github_user = prompt( "github user: ", _git_config("github.user") ); my $github_token = prompt( "github token: ", _git_config("github.token") ); my $github_repo_owner = prompt( "repo owner: ", $github_user ); my $github_repo = prompt( "repo name: ", path(".")->absolute->basename ); my $rt_user = prompt( "PAUSE ID: ", _pause_rc("user") ); my $rt_password = _pause_rc("password") ? _pause_rc("password") : prompt("PAUSE password: "); my $rt_dist = prompt( "RT dist name: ", _dist_name() ); my $gh = Net::GitHub->new( access_token => $github_token ); $gh->set_default_user_repo( $github_repo_owner, $github_repo ); my $gh_issue = $gh->issue; my $rt = RT::Client::REST->new( server => 'https://rt.cpan.org/' ); $rt->login( username => $rt_user, password => $rt_password ); # see which tickets we already have on the github side my @gh_issues = map { /\[rt\.cpan\.org #(\d+)\]/ } map { $_->{title} } $gh_issue->repos_issues( $github_repo_owner, $github_repo, { state => 'open' } ); my @rt_tickets = $rt->search( type => 'ticket', query => qq{ Queue = '$rt_dist' and ( Status = 'new' or Status = 'open' ) }, ); for my $id (@rt_tickets) { if ( any(@gh_issues) eq $id ) { say "ticket #$id already on github"; next; } # get the information from RT my $ticket = RT::Client::REST::Ticket->new( rt => $rt, id => $id, ); $ticket->retrieve; # we just want the first transaction, which # has the original ticket description my $desc = $ticket->transactions->get_iterator->()->content; $desc =~ s/^/ /gms; my $subject = $ticket->subject; my $isu = $gh_issue->create_issue( { "title" => "$subject [rt.cpan.org #$id]", "body" => "https://rt.cpan.org/Ticket/Display.html?id=$id\n\n$desc", } ); say "ticket #$id ($subject) copied to github"; }
A more sophisticated version would import each of the RT comments as github comments, but this was enough for me to use Github's issue tracker and not lose track of things that were previously in RT.
6 Comments
Sweet. I also had updated the script in my env repo (https://github.com/yanick/environment/blob/master/bin/rt-to-github.pl) earlier this january after rjbs poked me. I've updated the blog entry to point to that update and yours.
I worked with this some today. I think I've got support for exporting/importing comments working, but it needs to be tested. It doesn't support attachments, or transactions which have not "content", like changing the ticket the owner.
Before I run it with a real queue, I want to ask: What about the "requestor email"? This is an important bit of information, and as best as I can tell, Github offers no way to preserve it, at least not in a way that would cause a comment to seamlessly go to them.
My understanding is that the workaround is to continue to keep the RT ticket around, and reply back to them through that ticket, and encourage them to continue the conversation on Github if they are interested and able. Is that right? I'll have to consider this complication as I consider importing 50+ issues.
Very cool!
Unfortunately, I think the requestor email get lost. I don't think GH lets you create a commit with someone else as an owner.
Maybe email the requestor with the new GH issue link and tell them how to watch the issue? (Click the watch button or whatever.)
I think it will be lossy, regardless, but I'm willing to live with that for many (but not all) dists because the contribution benefits are worth it.
Mark, maybe your script could have an option to close the RT ticket with a comment pointing people to the new GitHub ticket. This would have RT send them a notice and if they were still concerned they could follow that ticket on github.
Good idea. I was going to figure out how to do a "bulk update", but scripting it may be just about as easy.
I have a few "stalled" tickets on rt.cpan.org that would also be nice to port over, and currently aren't handled. I'm sure those will be worth automating for me.
Regarding handling the requestor email:
- I bet I can at least find it and stick it in the GitHub issue for reference, even if I can't reply to it directly.
- In the long term, having a Github account seems like no higher of a bar than having an RT account. I don't think the create-by-email interface of RT is used that much, as it it's spam liability.
I meant to post my updated version. Here it is:
https://gist.github.com/markstos/5096483