How to script git with perl and Git::Wrapper

If you work with git, eventually you will want to script something that git doesn't do already and can't be accomplished with an alias. If you use Perl, the Git::Wrapper module makes scripting git easy.

The big advantage of Git::Wrapper is that it wraps the binary version of git, which keeps your perl and git independent. Upgrade perl and not git? Add Git::Wrapper and you're done. Upgrade git while keeping your existing perl? No problem. If you get your git from an OS distribution package, but roll your own perl, then Git::Wrapper is for you!

Git::Wrapper just makes it really easy to pass git commands and get back useful data. It really shines on "git log" commands, as it parses each commit message into Git::Wrapper::Log objects with accessors for id, author, date, and message. For everything else, it's not much more than a wrapper around qx(), but it returns arrays of lines and throws exceptions when things go wrong, which just saves a little time and code.

Here is a short example that I whipped up recently to automate the process of adding a github repository remote for someone when they send me a pull request. (It also uses my Getopt::Lucid module, but as the name suggests, it should be clear what that does.)

#!/usr/bin/env perl
use 5.010;
use strict;
use warnings;
use autodie;
use Git::Wrapper;
use Getopt::Lucid qw/:all/;

# USAGE: github-remote [-r REPO] [NAME]
#     NAME -- a github user name (or defaults to 'dagolden')
#     REPO -- a github repository path (or is parsed out of my private origin repo)

my $opts = Getopt::Lucid->getopt([
  Param('repo|r'),
]);

my $who = shift @ARGV;

# Get a wrapper for the current directory
my $git = Git::Wrapper->new(".");

# Locate the origin repository URL
my ($origin) = grep { /origin/ } $git->remote("-v");
die "Couldn't determine origin\n" unless $origin;
$origin =~ s/^origin\s+//;
$origin =~ s/\s+\(.*$//;

# Parse out the repository path
my $repo = $opts->get_repo;
unless ( $repo ) {
  # me@git.example.com:my-repo-name.git
  ($repo) = $origin =~ m/^[^:]+:(.+)$/;
}

# Set up someone else's github repo
if ( $who ) {
  $git->remote("add", $who, "git://github.com/${who}/${repo}");
}
# Or set up my own github mirror
else {
  $git->remote("add", "github", "git\@github.com:dagolden/${repo}");
}

That's it. I admit the program is a little crufty because of how I manage my repositories with a private origin that I mirror to a github repository, but there's almost no cruft around interactions with git. Consider this:

  $git->remote("add", $who, "git://github.com/${who}/${repo}");

If the command fails (if there's already $who as a remote name), an exception will be thrown. That means I don't have to write any error handling myself, which is perfect for a simple automation program like this one.

Posted in Uncategorized, git, perl-programming | Tagged , | Leave a comment

Are you ready for CPAN Testers 2.0?

If you haven't seen the official notice, the CPAN Testers project is migrating from using email to send reports (CPAN Testers 1.0) to using web-based submission (CPAN Testers 2.0). As of September 1, 2010, the old email list will be shut down and only web submissions will be accepted.

I've just updated the "quick-start" documentation on the CPAN Testers wiki: Quick HowTo for CPAN Testers 2.0

It's not perfect, but it's a lot better than it used to be. If you are still submitting reports by email, please read and follow the instructions before the end of August. If you have trouble, please ask for help on the cpan-testers-discuss mailing list or on #cpantesters-discuss on irc.perl.org.

Posted in cpan-testers, perl-programming | Tagged , | Leave a comment

If Perl were smarter about references

I wish that Perl 5 could be smarter about references. I wish I could just give an array reference to functions that only act on arrays and have Perl do the dereferencing for me.

Here is what I have to write today to use array functions on an array reference:

# Given $obj->foo() that returns an array reference

shift @{ $obj->foo };
push @{ $obj->foo }, @stuff;
splice @{ $obj->foo }, 0, 2;

Here is how I wish it would work:

shift $obj->foo;
push $obj->foo, @stuff;
splice $obj->foo, 0, 2;

Isn't that second block a lot cleaner to read? Getting rid of the @{} declutters the code and, at least for me, it's still clear from the context that I expect the first argument to act like an array.

Likewise, I'd like to have the same magic work for functions like values or keys, which would let me flatten array references or get an index list in a more readable way:

# from this:
for ( @{ $obj->foo } ) { ... }
for ( $#{ $obj->foo } ) { ... }

# to this:
for ( values $obj->foo ) { ... }
for ( keys $obj->foo ) { ... }

I suspect something similar would make sense for hash references, too.

On reflection, I think this would mean that checking that the first argument indeed held an array reference would have to be done at run-time, but I suspect there might be a way within the Perl op-codes to avoid such a checks except when necessary. (Thus, you could still use @{} for a slight bit of hand-optimization, if desired.)

I wish I knew more Perl guts to work on a draft implementation, because right now I think this would be one of the coolest features to see in a future Perl.

Posted in perl-programming | Tagged , | 8 Comments

Thoughts on Perl 6 hype and backlash

The news of "Rakudo Star" (a Perl 6 compiler) has spawned enough hype to prompt a backlash. I haven't entirely decided whether I think this is significant or not, much less good or bad for the Perl community, so this post is my attempt to organize my thoughts on it.

Factually, Rakudo is not new -- there have been thirty-one development releases of it. But Rakudo Star is the first prototype of an end-user distribution tarball that includes the latest Rakudo, the Parrot virtual machine, existing documentation, build instructions and so on. I would say it is designed for Perl 6 experimenters, rather than Perl 6 implementors. It means that anyone interested in Perl 6 can now just do this:

$ tar xzf rakudo-star-2010.07.tar.gz
$ cd rakudo-star-2010.07/
$ more README
... satisfy library prereqs ...
$ perl Configure.pl --gen-parrot
$ make
$ make install
$ ./perl6 -e 'say "Hello World"'

For the Perl 6 project team -- or more broadly, anyone carrying the Perl 6 torch for the last ten years -- the release of Rakudo Star has huge symbolic importance. They're saying, in effect, "look at this cool project we've been working on! We want you to get as excited as we are about what Perl 6 will be able to do". And, you know what, if I were working on something for ten years, I'd be wanting to celebrate, too.

For those outside the project team, the significance of the release isn't so obvious. I can imagine a wide range of reactions:

  • "Cool! I've been wanting to try some Perl 6 but didn't know where to start."
  • "What's the big deal? I can't use it for anything real yet."
  • "This sucks. It's too (big|slow|buggy|incomplete)."
  • "Gee, thanks. Perl 6 gets the attention and Perl 5 gets ignored... again"
  • "Perl 6?"

There is no way reactions to Rakudo Star can possibly live up to the hopes and dreams of those involved the project. I'm not surprised that some of the project members seem defensive in the face of criticism.

Leaving the emotional reactions aside, Rakudo Star still is a significant step forward for Perl 6, as it means inviting a less dedicated group of participants to test and react to the evolution of both the language and the implementation. Some of that will be negative, but some will be positive. More importantly, it will provide focus to the project going forward and shift the mentality -- gradually, one hopes -- from "get it absolutely right" to "get it done".

In the past, I've felt that the Perl 6 effort has been a distraction for Perl 5. On reflection, I've decided that the release of Rakudo Star is good for Perl 5 because it makes it easier for people to assess the relative status of both. Now, more than ever, it's clear that Perl 6 is not going to sweep Perl 5 away any time soon, but people who are interested in the comparison can watch both evolve and decide where they want to invest their time.

It's also pretty clear that Perl 6 is not going to make substantial inroads against Python, Ruby, PHP and other dynamic languages any time soon, either. If anyone was waiting for Perl 6 to rescue Perl, then they'll need to keep waiting.

However, Rakudo Star will help demonstrate common strengths of the current incarnation of both Perl 5 and Perl 6 language development.

  • Timeboxed delivery -- both projects are now delivering regular, packaged development releases that users can unpack, install and explore to see how new features evolve and how it works with previously written code.
  • Tests and a testing culture -- both Perl 5 and Perl 6 are backed by thousands of tests that help catch potential errors or behavior regressions
  • CPAN -- Perl 5 already has it (and it's adding 1,000 distributions a month) and some of the early work by the Perl 6 community has been to start porting some of the more popular Perl 5 modules into Perl 6.
  • Community -- there are dozens (or more) Perl conferences each year. These have often included talks on Perl 6 and this trend will likely continue. While Perl 5 and Perl 6 are different languages, they share not only a common heritage but a common culture as well

I suggest that people put the release of Rakudo Star in perspective. It is not a game-changer, but it is still a big step forward. Those who delivered it deserve kudos not brickbats, and I look forward to a next release that is even better.

Posted in parrot, perl-programming, perl6 | Tagged , | 8 Comments

Creating new distributions with Dist::Zilla

You may already know that Dist::Zilla can create new boilerplate Perl distributions, just like Module::Starter (and other boilerplate generators).

$ dzil new My::Module

If you've ever tried this, you've seen that the built-in boilerplate is very, very minimal and you probably want to know how to customize it to fit your dzil style.

I wrote up the tutorial below and submitted it for consideration for dzil.org. If you use dzil, I hope you'll find it to be a useful step-by-step guide. Feedback is welcome, either in comments below or via my dzil.org branch on github. I also want to thank xenoterracide for writing "Creating new projects with dzil new and templates", which was a huge help to me in figuring out how this works. Several of the examples below were taken or adapted from his article.

Customizing the Minting Process

Dist::Zilla lets you create any number of "minting profiles" to create distributions with different default contents. You might have one for work and one for personal code; one for object-oriented code and one for functional code; or one for web applications and one for command-line applications, etc.

Preparation

For the tasks below you'll want to:

  • install Dist::Zilla, version 4.101780 or later
  • run "dzil setup"

The rest of this document assumes that you haven't already made a custom minting profile, meaning that you haven't got anything in ~/.dzil/profiles. If you have a default profile, just rename it if you want to follow the steps exactly as described below.

Create a default profile

Create a directory called ~/.dzil/profiles/default. This will hold your default minting profile, overriding the very simple default that ships with Dist::Zilla.

The first thing you want to do is change into that directory and create a profile.ini file with the following content:

[TemplateModule/:DefaultModuleMaker]
template = Module.pm

[GatherDir::Template]
root = skel
include_dotfiles = 1

The profile.ini file defines two components for minting your new distribution. The first one, TemplateModule, is given a special name :DefaultModuleMaker, to tell Dist::Zilla that this component will be used for creating the new module file. The template parameter tells it a file to use, so create a Module.pm file (in the same directory as profile.ini) with the following content:

use strict;
use warnings;
package {{$name}};

1;

The second component in profile.ini is GatherDir::Template, which specifies a directory of templates that will also be created in your new distribution. That is where you'll be putting your custom dist.ini, Changes or other files.

Create ~/.dzil/profiles/default/skel and change to that directory.

Next, create a dist.ini file with the following content:

{{
    $license = ref $dist->license;
    if ( $license =~ /^Software::License::(.+)$/ ) {
        $license = $1;
    } else {
        $license = "=$license";
    }

    $authors = join( "\n", map { "author  = $_" } @{$dist->authors} );
    $copyright_year = (localtime)[5] + 1900;
    '';
}}name    = {{$dist->name}}
{{$authors}}
license = {{$license}}
copyright_holder = {{$dist->copyright_holder}}
copyright_year   = {{$copyright_year}}

[@Basic]

Ignore all the gobbledy-gook at the top and notice the [@Basic] line. this will give you a dist.ini in your new directory that uses the @Basic plugin bundle. As you become more familiar with Dist::Zilla, this is where you'll want to add more plugins to customize what Dist::Zilla can do for you.

Change to your work directory and run dzil new My::Module. You should get a new distribution that is almost exactly like the original default included with Dist::Zilla, except that it has the addition of the @Basic plugin bundle in the new dist.ini file.

Now, all you have to do is customize the dist.ini and Module.pm files in minting profile to suit your own personal style.

Enhancing your minting profile

You can create other files in skel as well. For example, if you use git, you might want to create a starter .gitignore:

/{{$dist->name}}*
.build

Or, if you start using the NextRelease plugin, you probably want to have a Changes file that takes advantage of it:

Revision history for {{$dist->name}}

{{ '{{$NEXT}}' }}
    -

In the sample above, you can see the syntax for including template text inside a template, which might be useful for other skeleton files as well.

Setting up an alternate minting profile

The easiest way to set up an alternate profile is to copy your default profile to a new directory. For example, if you wanted to set up a profile that provided a Moose-specific starter module, you could create a copy like this:

$ cp -a ~/.dzil/profiles/default ~/.dzil/profiles/moose

Then, customize the ~/.dzil/profiles/moose/Module.pm file to load Moose and provide any boilerplate you like.

You can run dzil new with the -p flag to use the alternate profile:

$ dzil new -p moose My::Moose::Module
Posted in dzil, perl-programming | Tagged , | 6 Comments

© 2009-2010 David Golden All Rights Reserved