Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Dist::Zilla — raaaaaaaaar!

Dist::Zilla — raaaaaaaaar!

Perl's CPAN system is its killer app: a massive collection of libraries for
nearly any task at hand. The code on the CPAN ranges from dreadful to superb,
but the code used to build CPAN packages has typically hovered around
"mediocre," largely due to artificial constraints. Dist::Zilla breaks free of
constraints like performance, footprint, and good taste to provide you with
unbridled power.

Ricardo Signes

September 12, 2009
Tweet

More Decks by Ricardo Signes

Other Decks in Programming

Transcript

  1. RJBS - cake and pie (and doughnuts) - cocktails and

    beer - shooting zombies - giving away useful software
  2. WriteMakefile( NAME => ‘YourApp’, AUTHOR => ‘You <[email protected]>’, VERSION_FROM =>

    ‘lib/YourApp.pm’, ABSTRACT_FROM => ‘lib/YourApp.pm’, LICENSE => ‘perl’, PREREQ_PM => { ‘Lingua::EN::Inflect’ => 1.86, ‘Sub::Override’ => 0.07, ‘Test::More’ => 0, }, );
  3. WriteMakefile( NAME => ‘YourApp’, AUTHOR => ‘You <[email protected]>’, VERSION_FROM =>

    ‘lib/YourApp.pm’, ABSTRACT_FROM => ‘lib/YourApp.pm’, LICENSE => ‘perl’, PREREQ_PM => { ‘Lingua::EN::Inflect’ => 1.86, ‘Sub::Override’ => 0.07, ‘Test::More’ => 0, }, (eval { ExtUtils::MakeMaker->VERSION(6.46) } ? (META_MERGE => { resources => { Repository => ‘...’ } }) : ()), );
  4. ./Changes ./LICENSE ./MANIFEST.SKIP ./Makefile.PL ./README ./lib/YourApp.pm ./lib/YourApp/Reticulator.pm ./lib/YourApp/Util/mtfnpy.pm ./lib/YourApp/Xyzzy.pm ./t/unit-tests.t

    ./t/pod-coverage.t ./t/pod.t ./LICENSE ./MANIFEST.SKIP ./Makefile.PL ./README ./lib/YourApp.pm ./lib/YourApp/Reticulator.pm
  5. ./Changes ./LICENSE ./MANIFEST.SKIP ./Makefile.PL ./README ./lib/YourApp.pm ./lib/YourApp/Reticulator.pm ./lib/YourApp/Util/mtfnpy.pm ./lib/YourApp/Xyzzy.pm ./t/unit-tests.t

    ./t/pod-coverage.t ./t/pod.t ./LICENSE ./MANIFEST.SKIP ./Makefile.PL ./README ./lib/YourApp.pm ./lib/YourApp/Reticulator.pm ./lib/YourApp/Util/mtfnpy.pm
  6. ./Changes ./LICENSE ./MANIFEST.SKIP ./Makefile.PL ./README ./lib/YourApp.pm ./lib/YourApp/Reticulator.pm ./lib/YourApp/Util/mtfnpy.pm ./lib/YourApp/Xyzzy.pm ./t/unit-tests.t

    ./t/pod-coverage.t ./t/pod.t ./LICENSE ./MANIFEST.SKIP ./Makefile.PL ./README ./lib/YourApp.pm ./lib/YourApp/Reticulator.pm ./lib/YourApp/Util/mtfnpy.pm ./lib/YourApp/Xyzzy.pm
  7. ./Changes ./LICENSE ./MANIFEST.SKIP ./Makefile.PL ./README ./lib/YourApp.pm ./lib/YourApp/Reticulator.pm ./lib/YourApp/Util/mtfnpy.pm ./lib/YourApp/Xyzzy.pm ./t/unit-tests.t

    ./t/pod-coverage.t ./t/pod.t ./LICENSE ./MANIFEST.SKIP ./Makefile.PL ./README ./lib/YourApp.pm ./lib/YourApp/Reticulator.pm ./lib/YourApp/Util/mtfnpy.pm ./lib/YourApp/Xyzzy.pm ./t/unit-tests.t
  8. ./Changes ./LICENSE ./MANIFEST.SKIP ./Makefile.PL ./README ./lib/YourApp.pm ./lib/YourApp/Reticulator.pm ./lib/YourApp/Util/mtfnpy.pm ./lib/YourApp/Xyzzy.pm ./t/unit-tests.t

    ./t/pod-coverage.t ./t/pod.t ./LICENSE ./MANIFEST.SKIP ./Makefile.PL ./README ./lib/YourApp.pm ./lib/YourApp/Reticulator.pm ./lib/YourApp/Util/mtfnpy.pm ./lib/YourApp/Xyzzy.pm ./t/unit-tests.t ./t/pod-coverage.t
  9. ./Changes ./LICENSE ./MANIFEST.SKIP ./Makefile.PL ./README ./lib/YourApp.pm ./lib/YourApp/Reticulator.pm ./lib/YourApp/Util/mtfnpy.pm ./lib/YourApp/Xyzzy.pm ./t/unit-tests.t

    ./t/pod-coverage.t ./t/pod.t ./LICENSE ./MANIFEST.SKIP ./Makefile.PL ./README ./lib/YourApp.pm ./lib/YourApp/Reticulator.pm ./lib/YourApp/Util/mtfnpy.pm ./lib/YourApp/Xyzzy.pm ./t/unit-tests.t ./t/pod-coverage.t ./t/pod.t
  10. ./Changes ./LICENSE ./MANIFEST.SKIP ./Makefile.PL ./README ./lib/YourApp.pm ./lib/YourApp/Reticulator.pm ./lib/YourApp/Util/mtfnpy.pm ./lib/YourApp/Xyzzy.pm ./t/unit-tests.t

    ./t/pod-coverage.t ./t/pod.t ./Changes ./lib/YourApp.pm ./lib/YourApp/Reticulator.pm ./lib/YourApp/Util/mtfnpy.pm
  11. ./Changes ./LICENSE ./MANIFEST.SKIP ./Makefile.PL ./README ./lib/YourApp.pm ./lib/YourApp/Reticulator.pm ./lib/YourApp/Util/mtfnpy.pm ./lib/YourApp/Xyzzy.pm ./t/unit-tests.t

    ./t/pod-coverage.t ./t/pod.t ./Changes ./lib/YourApp.pm ./lib/YourApp/Reticulator.pm ./lib/YourApp/Util/mtfnpy.pm ./lib/YourApp/Xyzzy.pm
  12. - updating version numbers - uploading to CPAN - committing,

    tagging releases in Git (VCS) - determine prerequisites
  13. - updating version numbers - uploading to CPAN - committing,

    tagging releases in Git (VCS) - determine prerequisites - other stuff that is totally boring
  14. - eating cake and pie (and donuts) - drinking cocktails

    and beer - shooting zombies - giving away useful software
  15. - Dist::Zilla only handles “make dist” - it doesn’t handle

    “make install” - only authors need to install the 106 prereqs
  16. - Dist::Zilla only handles “make dist” - it doesn’t handle

    “make install” - only authors need to install the 106 prereqs - your users don’t see Dist::Zilla
  17. - Dist::Zilla only handles “make dist” - it doesn’t handle

    “make install” - only authors need to install the 106 prereqs - your users don’t see Dist::Zilla - they just see MakeMaker or Module::Build
  18. # How Dist::Zilla builds a dist: prepare; gather_files; prune_files; munge_files;

    register_prereqs; setup_installer; write_files; finish; name = Your-Dist license = Perl_5 copyright_holder = J. Fred Bloggs [GatherDir] include_dotfiles = 1 [PruneCruft] [MetaYAML] [MakeMaker] [TestRelease] [ConfirmRelease] [UploadToCPAN] user = JFB password = QwardRules
  19. # How Dist::Zilla builds a dist: prepare; gather_files; prune_files; munge_files;

    register_prereqs; setup_installer; write_files; finish; name = Your-Dist license = Perl_5 copyright_holder = J. Fred Bloggs [GatherDir] include_dotfiles = 1 [PruneCruft] [MetaYAML] [MakeMaker] [TestRelease] [ConfirmRelease] [UploadToCPAN] user = JFB password = QwardRules
  20. # How Dist::Zilla builds a dist: prepare; gather_files; prune_files; munge_files;

    register_prereqs; setup_installer; write_files; finish; name = Your-Dist license = Perl_5 copyright_holder = J. Fred Bloggs [GatherDir] include_dotfiles = 1 [PruneCruft] [MetaYAML] [MakeMaker] [TestRelease] [ConfirmRelease] [UploadToCPAN] user = JFB password = QwardRules
  21. # How Dist::Zilla builds a dist: prepare; gather_files; prune_files; munge_files;

    register_prereqs; setup_installer; write_files; finish; name = Your-Dist license = Perl_5 copyright_holder = J. Fred Bloggs [GatherDir] include_dotfiles = 1 [PruneCruft] [MetaYAML] [MakeMaker] [TestRelease] [ConfirmRelease] [UploadToCPAN] user = JFB password = QwardRules
  22. name = Dist-Zilla author = Ricardo SIGNES <[email protected]> license =

    Perl_5 copyright_holder = Ricardo SIGNES [@Basic] [AutoPrereq] [AutoVersion] [PkgVersion] [MetaConfig] [MetaJSON] [NextRelease] [PodSyntaxTests] [Repository] [PodWeaver] [@Git]
  23. name = Dist-Zilla author = Ricardo SIGNES <[email protected]> license =

    Perl_5 copyright_holder = Ricardo SIGNES [@Basic] [AutoPrereq] [AutoVersion] [PkgVersion] [MetaConfig] [MetaJSON] [NextRelease] [PodSyntaxTests] [Repository] [PodWeaver] [@Git]
  24. name = Dist-Zilla author = Ricardo SIGNES <[email protected]> license =

    Perl_5 copyright_holder = Ricardo SIGNES [@Basic] [AutoPrereq] [AutoVersion] [PkgVersion] [MetaConfig] [MetaJSON] [NextRelease] [PodSyntaxTests] [Repository] [PodWeaver] [@Git]
  25. sub build { my ($self) = @_; my @files =

    $self->gather_files; How Dist::Zilla Builds
  26. sub build { my ($self) = @_; my @files =

    $self->gather_files; $self->munge_files( @files ); How Dist::Zilla Builds
  27. sub build { my ($self) = @_; my @files =

    $self->gather_files; $self->munge_files( @files ); $self->collect_metadata; How Dist::Zilla Builds
  28. sub build { my ($self) = @_; my @files =

    $self->gather_files; $self->munge_files( @files ); $self->collect_metadata; $self->write_out( @files ); How Dist::Zilla Builds
  29. sub build { my ($self) = @_; my @files =

    $self->gather_files; $self->munge_files( @files ); $self->collect_metadata; $self->write_out( @files ); } How Dist::Zilla Builds
  30. [GatherDir] [PruneCruft] [Manifest] [ManifestSkip] [MakeMaker] [License] [Readme] [MetaYAML] [ExtraTests] [ExecDir]

    [ShareDir] [TestRelease] [ConfirmRelease] [UploadToCPAN] MANIFEST The Basics
  31. [GatherDir] [PruneCruft] [Manifest] [ManifestSkip] [MakeMaker] [License] [Readme] [MetaYAML] [ExtraTests] [ExecDir]

    [ShareDir] [TestRelease] [ConfirmRelease] [UploadToCPAN] MANIFEST Makefile.PL The Basics
  32. [GatherDir] [PruneCruft] [Manifest] [ManifestSkip] [MakeMaker] [License] [Readme] [MetaYAML] [ExtraTests] [ExecDir]

    [ShareDir] [TestRelease] [ConfirmRelease] [UploadToCPAN] MANIFEST Makefile.PL LICENSE The Basics
  33. [GatherDir] [PruneCruft] [Manifest] [ManifestSkip] [MakeMaker] [License] [Readme] [MetaYAML] [ExtraTests] [ExecDir]

    [ShareDir] [TestRelease] [ConfirmRelease] [UploadToCPAN] MANIFEST Makefile.PL LICENSE README The Basics
  34. [GatherDir] [PruneCruft] [Manifest] [ManifestSkip] [MakeMaker] [License] [Readme] [MetaYAML] [ExtraTests] [ExecDir]

    [ShareDir] [TestRelease] [ConfirmRelease] [UploadToCPAN] MANIFEST Makefile.PL LICENSE README META.yml The Basics
  35. package YourApp; =head1 NAME YourApp - my awesome app =head1

    VERSION version 1.001 =cut our $VERSION = 0.001; =head1 DESCRIPTION This app is awesome. =head1 METHODS =head2 this_method This method does stuff. =cut method this_method { ... } =head2 that_method Also stuff. =cut method that_method { ... } =head1 AUTHOR Margo Yapp <[email protected]> =head1 LICENSE Copyright (C) 2008, Margo Yapp. This is distributed under the terms of the accidental death and dismemberment license and if you redistribuet this document you will be “accidentally” deathed or dismembered. You have been told. =cut 1;
  36. package YourApp; =head1 NAME YourApp - my awesome app =head1

    VERSION version 1.001 =cut our $VERSION = 0.001; =head1 DESCRIPTION This app is awesome. =head1 METHODS =head2 this_method This method does stuff. =cut method this_method { ... } =head2 that_method Also stuff. =cut method that_method { ... } =head1 AUTHOR Margo Yapp <[email protected]> =head1 LICENSE Copyright (C) 2008, Margo Yapp. This is distributed under the terms of the accidental death and dismemberment license and if you redistribuet this document you will be “accidentally” deathed or dismembered. You have been told. =cut 1;
  37. package YourApp; =head1 NAME YourApp - my awesome app =head1

    VERSION version 1.001 =cut our $VERSION = 0.001; =head1 DESCRIPTION This app is awesome. =head1 METHODS =head2 this_method This method does stuff. =cut method this_method { ... } =head2 that_method Also stuff. =cut method that_method { ... } =head1 AUTHOR Margo Yapp <[email protected]> =head1 LICENSE Copyright (C) 2008, Margo Yapp. This is distributed under the terms of the accidental death and dismemberment license and if you redistribuet this document you will be “accidentally” deathed or dismembered. You have been told. =cut 1;
  38. package YourApp; # ABSTRACT: my awesome app =head1 VERSION version

    1.001 =cut our $VERSION = 0.001; =head1 DESCRIPTION This app is awesome. =head1 METHODS =head2 this_method This method does stuff. =cut method this_method { ... } =head2 that_method Also stuff. =cut method that_method { ... } =head1 AUTHOR Margo Yapp <[email protected]> =head1 LICENSE Copyright (C) 2008, Margo Yapp. This is distributed under the terms of the accidental death and dismemberment license and if you redistribuet this document you will be “accidentally” deathed or dismembered. You have been told. =cut 1;
  39. package YourApp; # ABSTRACT: my awesome app =head1 VERSION version

    1.001 =cut our $VERSION = 0.001; =head1 DESCRIPTION This app is awesome. =head1 METHODS =head2 this_method This method does stuff. =cut method this_method { ... } =head2 that_method Also stuff. =cut method that_method { ... } =head1 AUTHOR Margo Yapp <[email protected]> =head1 LICENSE Copyright (C) 2008, Margo Yapp. This is distributed under the terms of the accidental death and dismemberment license and if you redistribuet this document you will be “accidentally” deathed or dismembered. You have been told. =cut 1;
  40. package YourApp; # ABSTRACT: my awesome app our $VERSION =

    0.001; =head1 DESCRIPTION This app is awesome. =head1 METHODS =head2 this_method This method does stuff. =cut method this_method { ... } =head2 that_method Also stuff. =cut method that_method { ... } =head1 AUTHOR Margo Yapp <[email protected]> =head1 LICENSE Copyright (C) 2008, Margo Yapp. This is distributed under the terms of the accidental death and dismemberment license and if you redistribuet this document you will be “accidentally” deathed or dismembered. You have been told. =cut 1;
  41. package YourApp; # ABSTRACT: my awesome app our $VERSION =

    0.001; =head1 DESCRIPTION This app is awesome. =head1 METHODS =head2 this_method This method does stuff. =cut method this_method { ... } =head2 that_method Also stuff. =cut method that_method { ... } =head1 AUTHOR Margo Yapp <[email protected]> =head1 LICENSE Copyright (C) 2008, Margo Yapp. This is distributed under the terms of the accidental death and dismemberment license and if you redistribuet this document you will be “accidentally” deathed or dismembered. You have been told. =cut 1;
  42. package YourApp; # ABSTRACT: my awesome app =head1 DESCRIPTION This

    app is awesome. =head1 METHODS =head2 this_method This method does stuff. =cut method this_method { ... } =head2 that_method Also stuff. =cut method that_method { ... } =head1 AUTHOR Margo Yapp <[email protected]> =head1 LICENSE Copyright (C) 2008, Margo Yapp. This is distributed under the terms of the accidental death and dismemberment license and if you redistribuet this document you will be “accidentally” deathed or dismembered. You have been told. =cut 1;
  43. package YourApp; # ABSTRACT: my awesome app =head1 DESCRIPTION This

    app is awesome. =method this_method This method does stuff. =cut method this_method { ... } =method that_method Also stuff. =cut method that_method { ... } =head1 AUTHOR Margo Yapp <[email protected]> =head1 LICENSE Copyright (C) 2008, Margo Yapp. This is distributed under the terms of the accidental death and dismemberment license and if you redistribuet this document you will be “accidentally” deathed or dismembered. You have been told. =cut 1;
  44. package YourApp; # ABSTRACT: my awesome app =head1 DESCRIPTION This

    app is awesome. =method this_method This method does stuff. =cut method this_method { ... } =method that_method Also stuff. =cut method that_method { ... } =head1 AUTHOR Margo Yapp <[email protected]> =head1 LICENSE Copyright (C) 2008, Margo Yapp. This is distributed under the terms of the accidental death and dismemberment license and if you redistribuet this document you will be “accidentally” deathed or dismembered. You have been told. =cut 1;
  45. package YourApp; # ABSTRACT: my awesome app =head1 DESCRIPTION This

    app is awesome. =method this_method This method does stuff. =cut method this_method { ... } =method that_method Also stuff. =cut method that_method { ... } =head1 LICENSE Copyright (C) 2008, Margo Yapp. This is distributed under the terms of the accidental death and dismemberment license and if you redistribuet this document you will be “accidentally” deathed or dismembered. You have been told. =cut 1;
  46. package YourApp; # ABSTRACT: my awesome app =head1 DESCRIPTION This

    app is awesome. =method this_method This method does stuff. =cut method this_method { ... } =method that_method Also stuff. method that_method { ... } =head1 LICENSE Copyright (C) 2008, Margo Yapp. This is distributed under the terms of the accidental death and dismemberment license and if you redistribuet this document you will be “accidentally” deathed or dismembered. You have been told. =cut 1;
  47. package YourApp; # ABSTRACT: my awesome app =head1 DESCRIPTION This

    app is awesome. =method this_method This method does stuff. =cut method this_method { ... } =method that_method Also stuff. =cut method that_method { ... } =cut 1;
  48. package YourApp; # ABSTRACT: my awesome app =head1 DESCRIPTION This

    app is awesome. =method this_method This method does stuff. =cut method this_method { ... } =method that_method Also stuff. =cut method that_method { ... } 1;
  49. package YourApp; # ABSTRACT: my awesome app =head1 DESCRIPTION This

    app is awesome. =method this_method This method does stuff. =cut method this_method { ... } =method that_method Also stuff. =cut method that_method { ... } 1;
  50. Converting Your Dist - create dist.ini - remove a bunch

    of files - delete a bunch of stuff from leftover files
  51. Converting Your Dist - create dist.ini - remove a bunch

    of files - delete a bunch of stuff from leftover files - and Dist::Zooky, too
  52. sub mint { my ($self) = @_; my @files =

    $self->gather_files; How Dist::Zilla Mints
  53. sub mint { my ($self) = @_; my @files =

    $self->gather_files; $self->munge_files( @files ); How Dist::Zilla Mints
  54. sub mint { my ($self) = @_; my @files =

    $self->gather_files; $self->munge_files( @files ); $self->make_module( $module ); How Dist::Zilla Mints
  55. sub mint { my ($self) = @_; my @files =

    $self->gather_files; $self->munge_files( @files ); $self->make_module( $module ); $self->write_out( @files ); How Dist::Zilla Mints
  56. sub mint { my ($self) = @_; my @files =

    $self->gather_files; $self->munge_files( @files ); $self->make_module( $module ); $self->write_out( @files ); } How Dist::Zilla Mints
  57. [%User] name = Ricardo Signes email = [email protected] [%Rights] license_class

    = Perl_5 copyright_holder = Ricardo Signes [%PAUSE] username = RJBS password = PeasAreDelicious
  58. Minting a New Dist - create config.ini (in ~/.dzil) -

    maybe configure or install a profile - run dzil new
  59. sub build { my ($self) = @_; my @files =

    $self->gather_files; $self->munge_files( @files ); $self->collect_metadata; $self->write_out( @files ); } How Dist::Zilla Builds
  60. my @mungers = grep { $_->does(‘FileMunger’) } $self->plugins; for my

    $plugin (@mungers) { $plugin->munge_files( @files ); } How Dist::Zilla Builds
  61. package Dist::Zilla::Plugin::BuiltOn; use Moose; with ‘Dist::Zilla::Role::FileGatherer’; sub gather_files { my

    ($self) = @_; my $file = Dist::Zilla::File::InMemory->new({ name => ‘built-on.txt’, BuiltOn
  62. package Dist::Zilla::Plugin::BuiltOn; use Moose; with ‘Dist::Zilla::Role::FileGatherer’; sub gather_files { my

    ($self) = @_; my $file = Dist::Zilla::File::InMemory->new({ name => ‘built-on.txt’, content => scalar `uname -a`, BuiltOn
  63. package Dist::Zilla::Plugin::BuiltOn; use Moose; with ‘Dist::Zilla::Role::FileGatherer’; sub gather_files { my

    ($self) = @_; my $file = Dist::Zilla::File::InMemory->new({ name => ‘built-on.txt’, content => scalar `uname -a`, }); BuiltOn
  64. package Dist::Zilla::Plugin::BuiltOn; use Moose; with ‘Dist::Zilla::Role::FileGatherer’; sub gather_files { my

    ($self) = @_; my $file = Dist::Zilla::File::InMemory->new({ name => ‘built-on.txt’, content => scalar `uname -a`, }); $self->add_file( $file ); BuiltOn
  65. package Dist::Zilla::Plugin::BuiltOn; use Moose; with ‘Dist::Zilla::Role::FileGatherer’; sub gather_files { my

    ($self) = @_; my $file = Dist::Zilla::File::InMemory->new({ name => ‘built-on.txt’, content => scalar `uname -a`, }); $self->add_file( $file ); } BuiltOn
  66. package Dist::Zilla::Plugin::BuiltOn; use Moose; with ‘Dist::Zilla::Role::FileGatherer’; has filename => (

    is => ‘ro’, default => ‘built-on.txt’, ); sub gather_files { my ($self) = @_; my $file = Dist::Zilla::File::InMemory->new({ name => $self->filename, content => scalar `uname -a`, }); $self->add_file( $file ); }
  67. package Dist::Zilla::Plugin::AddShebangs; use Moose; with ‘Dist::Zilla::Role::FileMunger’; sub munge_file { my

    ($self, $file) = @_; next unless $file->name =~ /\.pl$/; my $content = “#!$^X\n” AddShebangs
  68. package Dist::Zilla::Plugin::AddShebangs; use Moose; with ‘Dist::Zilla::Role::FileMunger’; sub munge_file { my

    ($self, $file) = @_; next unless $file->name =~ /\.pl$/; my $content = “#!$^X\n” . $file->content; AddShebangs
  69. package Dist::Zilla::Plugin::AddShebangs; use Moose; with ‘Dist::Zilla::Role::FileMunger’; sub munge_file { my

    ($self, $file) = @_; next unless $file->name =~ /\.pl$/; my $content = “#!$^X\n” . $file->content; $file->content( $content ); AddShebangs
  70. package Dist::Zilla::Plugin::AddShebangs; use Moose; with ‘Dist::Zilla::Role::FileMunger’; sub munge_file { my

    ($self, $file) = @_; next unless $file->name =~ /\.pl$/; my $content = “#!$^X\n” . $file->content; $file->content( $content ); } AddShebangs
  71. package Dist::Zilla::Plugin::AddBlame; use Moose; with ‘Dist::Zilla::Role::FileMunger’; sub munge_file { my

    ($self, $file) = @_; next unless $file->name =~ /\.p[lm]$/; my $content = “# BUILD BY $ENV{USER}\n” AddBlame
  72. package Dist::Zilla::Plugin::AddBlame; use Moose; with ‘Dist::Zilla::Role::FileMunger’; sub munge_file { my

    ($self, $file) = @_; next unless $file->name =~ /\.p[lm]$/; my $content = “# BUILD BY $ENV{USER}\n” . $file->content; AddBlame
  73. package Dist::Zilla::Plugin::AddBlame; use Moose; with ‘Dist::Zilla::Role::FileMunger’; sub munge_file { my

    ($self, $file) = @_; next unless $file->name =~ /\.p[lm]$/; my $content = “# BUILD BY $ENV{USER}\n” . $file->content; $file->content( $content ); AddBlame
  74. package Dist::Zilla::Plugin::AddBlame; use Moose; with ‘Dist::Zilla::Role::FileMunger’; sub munge_file { my

    ($self, $file) = @_; next unless $file->name =~ /\.p[lm]$/; my $content = “# BUILD BY $ENV{USER}\n” . $file->content; $file->content( $content ); } AddBlame