Dist-Zilla-Plugin-Git-2.025/0000755000175000017500000000000012416261571013564 5ustar cjmcjmDist-Zilla-Plugin-Git-2.025/lib/0000755000175000017500000000000012416261571014332 5ustar cjmcjmDist-Zilla-Plugin-Git-2.025/lib/Dist/0000755000175000017500000000000012416261571015235 5ustar cjmcjmDist-Zilla-Plugin-Git-2.025/lib/Dist/Zilla/0000755000175000017500000000000012416261571016310 5ustar cjmcjmDist-Zilla-Plugin-Git-2.025/lib/Dist/Zilla/PluginBundle/0000755000175000017500000000000012416261571020700 5ustar cjmcjmDist-Zilla-Plugin-Git-2.025/lib/Dist/Zilla/PluginBundle/Git.pm0000644000175000017500000000452712416261571021771 0ustar cjmcjm# # This file is part of Dist-Zilla-Plugin-Git # # This software is copyright (c) 2009 by Jerome Quelin. # # This is free software; you can redistribute it and/or modify it under # the same terms as the Perl 5 programming language system itself. # use 5.008; use strict; use warnings; package Dist::Zilla::PluginBundle::Git; { $Dist::Zilla::PluginBundle::Git::VERSION = '2.025'; } # ABSTRACT: all git plugins in one go use Moose; use Module::Runtime 'use_module'; with 'Dist::Zilla::Role::PluginBundle'; # bundle all git plugins my @names = qw{ Check Commit Tag Push }; my %multi; for my $name (@names) { my $class = "Dist::Zilla::Plugin::Git::$name"; use_module $class; @multi{$class->mvp_multivalue_args} = (); } sub mvp_multivalue_args { keys %multi; } sub bundle_config { my ($self, $section) = @_; #my $class = ( ref $self ) || $self; my $arg = $section->{payload}; my @config; for my $name (@names) { my $class = "Dist::Zilla::Plugin::Git::$name"; my %payload; foreach my $k (keys %$arg) { $payload{$k} = $arg->{$k} if $class->can($k); } push @config, [ "$section->{name}/$name" => $class => \%payload ]; } return @config; } __PACKAGE__->meta->make_immutable; no Moose; 1; __END__ =pod =encoding UTF-8 =head1 NAME Dist::Zilla::PluginBundle::Git - all git plugins in one go =head1 VERSION version 2.025 =head1 SYNOPSIS In your F: [@Git] changelog = Changes ; this is the default allow_dirty = dist.ini ; see Git::Check... allow_dirty = Changes ; ... and Git::Commit commit_msg = v%v%n%n%c ; see Git::Commit tag_format = %v ; see Git::Tag tag_message = %v ; see Git::Tag push_to = origin ; see Git::Push =head1 DESCRIPTION This is a plugin bundle to load all git plugins. It is equivalent to: [Git::Check] [Git::Commit] [Git::Tag] [Git::Push] The options are passed through to the plugins. =for Pod::Coverage bundle_config mvp_multivalue_args =head1 AUTHOR Jerome Quelin =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2009 by Jerome Quelin. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Dist-Zilla-Plugin-Git-2.025/lib/Dist/Zilla/Role/0000755000175000017500000000000012416261571017211 5ustar cjmcjmDist-Zilla-Plugin-Git-2.025/lib/Dist/Zilla/Role/Git/0000755000175000017500000000000012416261571017734 5ustar cjmcjmDist-Zilla-Plugin-Git-2.025/lib/Dist/Zilla/Role/Git/DirtyFiles.pm0000644000175000017500000001144612416261571022356 0ustar cjmcjm# # This file is part of Dist-Zilla-Plugin-Git # # This software is copyright (c) 2009 by Jerome Quelin. # # This is free software; you can redistribute it and/or modify it under # the same terms as the Perl 5 programming language system itself. # use 5.008; use strict; use warnings; package Dist::Zilla::Role::Git::DirtyFiles; { $Dist::Zilla::Role::Git::DirtyFiles::VERSION = '2.025'; } # ABSTRACT: provide the allow_dirty & changelog attributes use Moose::Role; use Moose::Autobox; use MooseX::Types::Moose qw{ Any ArrayRef Str RegexpRef }; use MooseX::Types::Path::Tiny 0.010 qw{ Paths to_Paths }; use Moose::Util::TypeConstraints; use namespace::autoclean; use List::Util 'first'; use Path::Tiny 0.048 qw(); # subsumes use Try::Tiny; requires qw(log_fatal repo_root zilla); # -- attributes { # We specifically allow the empty string to represent the empty list. # Otherwise, there'd be no way to specify an empty list in an INI file. my $type = subtype as Paths; coerce($type, from ArrayRef, via { to_Paths( [ grep { length } @$_ ] ) }, from Any, via { length($_) ? to_Paths($_) : [] }, ); has allow_dirty => ( is => 'ro', lazy => 1, isa => $type, coerce => 1, builder => '_build_allow_dirty', ); } has changelog => ( is => 'ro', isa=>Str, default => 'Changes' ); { my $type = subtype as ArrayRef[RegexpRef]; coerce $type, from ArrayRef[Str], via { [map { qr/$_/ } @$_] }; has allow_dirty_match => ( is => 'ro', lazy => 1, coerce => 1, isa => $type, default => sub { [] }, ); } around mvp_multivalue_args => sub { my ($orig, $self) = @_; my @start = $self->$orig; return (@start, 'allow_dirty', 'allow_dirty_match'); }; # -- builders & initializers sub _build_allow_dirty { [ 'dist.ini', shift->changelog ] } around dump_config => sub { my $orig = shift; my $self = shift; my $config = $self->$orig; $config->{+__PACKAGE__} = { map { $_ => $self->$_ } qw(allow_dirty allow_dirty_match changelog), }; return $config; }; sub list_dirty_files { my ($self, $git, $listAllowed) = @_; my $git_root = $self->repo_root; my @filenames = $self->allow_dirty->flatten; if ($git_root ne '.') { # Interpret allow_dirty relative to the dzil root my $dzil_root = Path::Tiny::path($self->zilla->root)->absolute->realpath; $git_root = Path::Tiny::path($git_root) ->absolute($dzil_root) ->realpath; $self->log_fatal("Dzil root $dzil_root is not inside Git root $git_root") unless $git_root->subsumes($dzil_root); for my $fn (@filenames) { try { $fn = Path::Tiny::path($fn) ->absolute($dzil_root) ->realpath # process .. ->relative($git_root) ->stringify; }; } } # end if git root ne dzil root my $allowed = join '|', $self->allow_dirty_match->flatten, map { qr{^\Q$_\E$} } @filenames; $allowed = qr/(?!X)X/ if $allowed eq ''; # this cannot match anything return grep { /$allowed/ ? $listAllowed : !$listAllowed } $git->ls_files( { modified=>1, deleted=>1 } ); } # end list_dirty_files 1; __END__ =pod =encoding UTF-8 =head1 NAME Dist::Zilla::Role::Git::DirtyFiles - provide the allow_dirty & changelog attributes =head1 VERSION version 2.025 =head1 DESCRIPTION This role is used within the git plugin to work with files that are dirty in the local git checkout. =head1 ATTRIBUTES =head2 allow_dirty A list of paths that are allowed to be dirty in the git checkout. Defaults to C and the changelog (as defined per the C attribute. If your C is not the default (C<.>), then these pathnames are relative to Dist::Zilla's root directory, not the Git root directory. =head2 allow_dirty_match A list of regular expressions that match paths allowed to be dirty in the git checkout. This is combined with C. Defaults to the empty list. The paths being matched are relative to the Git root directory, even if your C is not the default (C<.>). =head2 changelog The name of the changelog. Defaults to C. =head1 METHODS =head2 list_dirty_files my @dirty = $plugin->list_dirty_files($git, $listAllowed); This returns a list of the modified or deleted files in C<$git>, filtered against the C attribute. If C<$listAllowed> is true, only allowed files are listed. If it's false, only files that are not allowed to be dirty are listed. In scalar context, returns the number of dirty files. =for Pod::Coverage mvp_multivalue_args =head1 AUTHOR Jerome Quelin =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2009 by Jerome Quelin. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Dist-Zilla-Plugin-Git-2.025/lib/Dist/Zilla/Role/Git/Repo.pm0000644000175000017500000000343712416261571021206 0ustar cjmcjm# # This file is part of Dist-Zilla-Plugin-Git # # This software is copyright (c) 2009 by Jerome Quelin. # # This is free software; you can redistribute it and/or modify it under # the same terms as the Perl 5 programming language system itself. # package Dist::Zilla::Role::Git::Repo; { $Dist::Zilla::Role::Git::Repo::VERSION = '2.025'; } # ABSTRACT: Provide repository information for Git plugins use Moose::Role; has 'repo_root' => ( is => 'ro', isa => 'Str', default => '.' ); my %cached_wrapper; around dump_config => sub { my $orig = shift; my $self = shift; my $config = $self->$orig; $config->{+__PACKAGE__} = { repo_root => $self->repo_root, }; return $config; }; sub git { my $root = shift->repo_root; $cached_wrapper{$root} ||= do { require Git::Wrapper; Git::Wrapper->new( $root ); }; } 1; __END__ =pod =encoding UTF-8 =head1 NAME Dist::Zilla::Role::Git::Repo - Provide repository information for Git plugins =head1 VERSION version 2.025 =head1 DESCRIPTION This role is used within the Git plugins to get information about the repository structure, and to create a Git::Wrapper object. =head1 ATTRIBUTES =head2 repo_root The repository root, either as a full path or relative to the distribution root. Default is C<.>. =head1 METHODS =head2 git $git = $plugin->git; This method returns a Git::Wrapper object for the C directory, constructing one if necessary. The object is shared between all plugins that consume this role (if they have the same C). =head1 AUTHOR Jerome Quelin =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2009 by Jerome Quelin. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Dist-Zilla-Plugin-Git-2.025/lib/Dist/Zilla/Plugin/0000755000175000017500000000000012416261571017546 5ustar cjmcjmDist-Zilla-Plugin-Git-2.025/lib/Dist/Zilla/Plugin/Git/0000755000175000017500000000000012416261571020271 5ustar cjmcjmDist-Zilla-Plugin-Git-2.025/lib/Dist/Zilla/Plugin/Git/NextVersion.pm0000644000175000017500000001727612416261571023130 0ustar cjmcjm# # This file is part of Dist-Zilla-Plugin-Git # # This software is copyright (c) 2009 by Jerome Quelin. # # This is free software; you can redistribute it and/or modify it under # the same terms as the Perl 5 programming language system itself. # use strict; use warnings; package Dist::Zilla::Plugin::Git::NextVersion; { $Dist::Zilla::Plugin::Git::NextVersion::VERSION = '2.025'; } # ABSTRACT: provide a version number by bumping the last git release tag use Dist::Zilla 4 (); use version 0.80 (); use Moose; use namespace::autoclean 0.09; use Path::Tiny qw(); use Try::Tiny; use Moose::Util::TypeConstraints; use constant _cache_fn => '.gitnxtver_cache'; with 'Dist::Zilla::Role::BeforeRelease'; with 'Dist::Zilla::Role::AfterRelease'; with 'Dist::Zilla::Role::FilePruner'; with 'Dist::Zilla::Role::VersionProvider'; with 'Dist::Zilla::Role::Git::Repo'; # -- attributes use constant _CoercedRegexp => do { my $tc = subtype as 'RegexpRef'; coerce $tc, from 'Str', via { qr/$_/ }; $tc; }; has version_regexp => ( is => 'ro', isa=> _CoercedRegexp, coerce => 1, default => sub { qr/^v(.+)$/ } ); has first_version => ( is => 'ro', isa=>'Str', default => '0.001' ); has version_by_branch => ( is => 'ro', isa=>'Bool', default => 0 ); sub _versions_from_tags { my ($regexp, $tags) = @_; # WARNING: The quotes in "$1" are necessary, because version doesn't # call get magic properly. return [ sort map { /$regexp/ ? try { version->parse("$1") } : () } @$tags ]; } # end _versions_from_tags has _all_versions => ( is => 'ro', isa=>'ArrayRef', init_arg => undef, lazy => 1, default => sub { my $self = shift; my $v = _versions_from_tags($self->version_regexp, [ $self->git->tag ]); if ($self->logger->get_debug) { $self->log_debug("Found version $_") for @$v; } $v; } ); sub _max_version { my $versions = shift; # arrayref of versions sorted in ascending order return $versions->[-1]->stringify if @$versions; return undef; } # end _max_version sub _last_version { my ($self) = @_; my $last_ver; my $by_branch = $self->version_by_branch; my $git = $self->git; local $/ = "\n"; # Force record separator to be single newline if ($by_branch) { my $head; my $cachefile = Path::Tiny::path(_cache_fn); if (-f $cachefile) { ($head) = $git->rev_parse('HEAD'); return $1 if $cachefile->slurp =~ /^\Q$head\E (.+)/; } try { # Note: git < 1.6.1 doesn't understand --simplify-by-decoration or %d my @tags; for ($git->rev_list(qw(--simplify-by-decoration --pretty=%d HEAD))) { /^\s*\((.+)\)/ or next; push @tags, split /,\s*/, $1; } # end for lines from git log s/^tag:\s+// for @tags; # Git 1.8.3 says "tag: X" instead of "X" my $versions = _versions_from_tags($self->version_regexp, \@tags); if ($self->logger->get_debug) { $self->log_debug("Found version $_ on branch") for @$versions; } $last_ver = _max_version($versions); }; if (defined $last_ver) { ($head) = $git->rev_parse('HEAD') unless $head; print { $cachefile->openw } "$head $last_ver\n"; return $last_ver; } } # end if version_by_branch # Consider versions from all branches: $last_ver = _max_version($self->_all_versions); $self->log("WARNING: Unable to find version on current branch") if defined($last_ver) and $by_branch; return $last_ver; } # -- role implementation around dump_config => sub { my $orig = shift; my $self = shift; my $config = $self->$orig; $config->{+__PACKAGE__} = { map { $_ => $self->$_ } qw(version_regexp first_version version_by_branch), }; return $config; }; sub before_release { my $self = shift; # Make sure we're not duplicating a version: my $version = version->parse( $self->zilla->version ); $self->log_fatal("version $version has already been tagged") if grep { $_ == $version } @{ $self->_all_versions }; } sub after_release { my $self = shift; # Remove the cache file, just in case: $self->zilla->root->file(_cache_fn)->remove; } sub provide_version { my ($self) = @_; # override (or maybe needed to initialize) return $ENV{V} if exists $ENV{V}; my $last_ver = $self->_last_version; return $self->first_version unless defined $last_ver; require Version::Next; my $new_ver = Version::Next::next_version($last_ver); $self->log("Bumping version from $last_ver to $new_ver"); return "$new_ver"; } sub prune_files { my $self = shift; for my $file (@{ $self->zilla->files }) { # Ensure we don't distribute .gitnxtver_cache: next unless $file->name eq _cache_fn; $self->log_debug([ 'pruning %s', $file->name ]); $self->zilla->prune_file($file); } return; } # end prune_files __PACKAGE__->meta->make_immutable; no Moose; 1; __END__ =pod =encoding UTF-8 =head1 NAME Dist::Zilla::Plugin::Git::NextVersion - provide a version number by bumping the last git release tag =head1 VERSION version 2.025 =head1 SYNOPSIS In your F: [Git::NextVersion] first_version = 0.001 ; this is the default version_by_branch = 0 ; this is the default version_regexp = ^v(.+)$ ; this is the default =head1 DESCRIPTION This does the L role. It finds the last version number from your Git tags, increments it using L, and uses the result as the C parameter for your distribution. In addition, when making a release, it ensures that the version being released has not already been tagged. (The L plugin has a similar check, but Git::Tag only checks for an exact match on the tag. Since Git::NextVersion knows how to extract version numbers from tags, it can find duplicates that Git::Tag would miss.) The plugin accepts the following options: =over =item * C - if the repository has no tags at all, this version is used as the first version for the distribution. It defaults to "0.001". =item * C - if true, consider only tags on the current branch when looking for the previous version. If you have a maintenance branch for stable releases and a development branch for trial releases, you should set this to 1. (You'll also need git version 1.6.1 or later.) The default is to look at all tags, because finding the tags reachable from a branch is a more expensive operation than simply listing all tags. =item * C - regular expression that matches a tag containing a version. It must capture the version into $1. Defaults to ^v(.+)$ which matches the default C from the L plugin. If you change C, you B set a corresponding C. =back You can also set the C environment variable to override the new version. This is useful if you need to bump to a specific version. For example, if the last tag is 0.005 and you want to jump to 1.000 you can set V = 1.000. $ V=1.000 dzil release Because tracing history takes time, if you use the C option, Git::NextVersion will create a F<.gitnxtver_cache> file in your repository to track the highest version number that is an ancestor of the HEAD revision. You should add F<.gitnxtver_cache> to your F<.gitignore> file. It will automatically be pruned from the distribution. =for Pod::Coverage provide_version prune_files before_release after_release =head1 AUTHOR Jerome Quelin =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2009 by Jerome Quelin. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Dist-Zilla-Plugin-Git-2.025/lib/Dist/Zilla/Plugin/Git/CommitBuild.pm0000644000175000017500000001717612416261571023053 0ustar cjmcjm# # This file is part of Dist-Zilla-Plugin-Git # # This software is copyright (c) 2009 by Jerome Quelin. # # This is free software; you can redistribute it and/or modify it under # the same terms as the Perl 5 programming language system itself. # use 5.008; use strict; use warnings; package Dist::Zilla::Plugin::Git::CommitBuild; { $Dist::Zilla::Plugin::Git::CommitBuild::VERSION = '2.025'; } # ABSTRACT: checkin build results on separate branch use Git::Wrapper 0.021 (); # need -STDIN use IPC::Open3; use IPC::System::Simple; # required for Fatalised/autodying system use File::chdir; use File::Spec::Functions qw/ rel2abs catfile /; use File::Temp; use Moose; use namespace::autoclean; use MooseX::AttributeShortcuts; use Path::Tiny qw(); use MooseX::Types::Path::Tiny qw( Path ); use MooseX::Has::Sugar; use MooseX::Types::Moose qw{ Str }; use Cwd qw(abs_path); use Try::Tiny; use String::Formatter ( method_stringf => { -as => '_format_branch', codes => { b => sub { shift->_source_branch }, }, }, method_stringf => { -as => '_format_message', codes => { b => sub { shift->_source_branch }, h => sub { (shift->git->rev_parse( '--short', 'HEAD' ))[0] }, H => sub { (shift->git->rev_parse('HEAD'))[0] }, t => sub { shift->zilla->is_trial ? '-TRIAL' : '' }, v => sub { shift->zilla->version }, } } ); # debugging... #use Smart::Comments '###'; with 'Dist::Zilla::Role::AfterBuild', 'Dist::Zilla::Role::AfterRelease'; with 'Dist::Zilla::Role::Git::Repo'; # -- attributes has branch => ( ro, isa => Str, default => 'build/%b', required => 1 ); has release_branch => ( ro, isa => Str, required => 0 ); has message => ( ro, isa => Str, default => 'Build results of %h (on %b)', required => 1 ); has release_message => ( ro, isa => Str, lazy => 1, builder => '_build_release_message' ); has build_root => ( rw, coerce => 1, isa => Path ); has _source_branch => ( is => 'ro', isa => 'Str', lazy => 1, init_arg=> undef, default => sub { ($_[0]->git->name_rev( '--name-only', 'HEAD' ))[0]; }, ); has multiple_inheritance => ( is => 'ro', isa => 'Bool', default => 0, ); # -- attribute builders sub _build_release_message { return shift->message; } # -- role implementation around dump_config => sub { my $orig = shift; my $self = shift; my $config = $self->$orig; $config->{+__PACKAGE__} = { map { $_ => $self->$_ } qw(branch release_branch message release_message build_root multiple_inheritance), }; return $config; }; sub after_build { my ( $self, $args) = @_; # because the build_root mysteriously change at # the 'after_release' stage $self->build_root( $args->{build_root} ); $self->_commit_build( $args, $self->branch, $self->message ); } sub after_release { my ( $self, $args) = @_; $self->_commit_build( $args, $self->release_branch, $self->release_message ); } sub _commit_build { my ( $self, undef, $branch, $message ) = @_; return unless $branch; my $dir = Path::Tiny->tempdir( CLEANUP => 1) ; my $src = $self->git; my $target_branch = _format_branch( $branch, $self ); for my $file ( @{ $self->zilla->files } ) { my ( $name, $content ) = ( $file->name, (Dist::Zilla->VERSION < 5 ? $file->content : $file->encoded_content) ); my ( $outfile ) = $dir->child( $name ); $outfile->parent->mkpath(); $outfile->spew_raw( $content ); chmod $file->mode, "$outfile" or die "couldn't chmod $outfile: $!"; } # returns the sha1 of the created tree object my $tree = $self->_create_tree($src, $dir); my ($last_build_tree) = try { $src->rev_parse("$target_branch^{tree}") }; $last_build_tree ||= 'none'; ### $last_build_tree if ($tree eq $last_build_tree) { $self->log("No changes since the last build; not committing"); return; } my @parents = ( ( $self->_source_branch ) x $self->multiple_inheritance, grep { eval { $src->rev_parse({ 'q' => 1, 'verify'=>1}, $_ ) } } $target_branch ); ### @parents my $this_message = _format_message( $message, $self ); my @commit = $src->commit_tree( { -STDIN => $this_message }, $tree, map { ( '-p' => $_) } @parents ); ### @commit $src->update_ref( 'refs/heads/' . $target_branch, $commit[0] ); } sub _create_tree { my ($self, $repo, $fs_obj) = @_; ### called with: "$fs_obj" if (!$fs_obj->is_dir) { my ($sha) = $repo->hash_object({ w => 1 }, "$fs_obj"); ### hashed: "$sha $fs_obj" return $sha; } my @entries; for my $obj ($fs_obj->children) { ### working on: "$obj" my $sha = $self->_create_tree($repo, $obj); my $mode = sprintf('%o', $obj->stat->mode); # $obj->is_dir ? '040000' : ' my $type = $obj->is_dir ? 'tree' : 'blob'; my $name = $obj->basename; push @entries, "$mode $type $sha\t$name"; } ### @entries my ($sha) = $repo->mktree({ -STDIN => join("\n", @entries, q{}) }); return $sha; } 1; __END__ =pod =encoding UTF-8 =head1 NAME Dist::Zilla::Plugin::Git::CommitBuild - checkin build results on separate branch =head1 VERSION version 2.025 =head1 SYNOPSIS In your F: [Git::CommitBuild] ; these are the defaults branch = build/%b message = Build results of %h (on %b) multiple_inheritance = 0 =head1 DESCRIPTION Once the build is done, this plugin will commit the results of the build to a branch that is completely separate from your regular code branches (i.e. with a different root commit). This potentially makes your repository more useful to those who may not have L and all of its dependencies installed. The plugin accepts the following options: =over 4 =item * branch - L string for where to commit the build contents. A single formatting code (C<%b>) is defined for this attribute and will be substituted with the name of the current branch in your git repository. Defaults to C, but if set explicitly to an empty string causes no build contents checkin to be made. =item * release_branch - L string for where to commit the build contents Same as C, but commit the build content only after a release. No default, meaning no release branch. =item * message - L string for what commit message to use when committing the results of the build. This option supports five formatting codes: =over 4 =item * C<%b> - Name of the current branch =item * C<%H> - Commit hash =item * C<%h> - Abbreviated commit hash =item * C<%v> - The release version number =item * C<%t> - The string "-TRIAL" if this is a trial release =back =item * release_message - L string for what commit message to use when committing the results of the release. Defaults to the same as C. =item * multiple_inheritance - Indicates whether the commit containing the build results should have the source commit as a parent. If false (the default), the build branch will be completely separate from the regular code branches. If set to a true value, commits on a build branch will have two parents: the previous build commit and the source commit from which the build was generated. =back =for Pod::Coverage after_build after_release =head1 AUTHOR Jerome Quelin =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2009 by Jerome Quelin. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Dist-Zilla-Plugin-Git-2.025/lib/Dist/Zilla/Plugin/Git/GatherDir.pm0000644000175000017500000001427612416261571022512 0ustar cjmcjm# # This file is part of Dist-Zilla-Plugin-Git # # This software is copyright (c) 2009 by Jerome Quelin. # # This is free software; you can redistribute it and/or modify it under # the same terms as the Perl 5 programming language system itself. # package Dist::Zilla::Plugin::Git::GatherDir; { $Dist::Zilla::Plugin::Git::GatherDir::VERSION = '2.025'; } # ABSTRACT: gather all tracked files in a Git working directory use Moose; use Moose::Autobox; use MooseX::Types::Path::Tiny qw(Path); extends 'Dist::Zilla::Plugin::GatherDir' => { -version => 4.200016 }; # exclude_match with 'Dist::Zilla::Role::Git::Repo'; use File::Spec; use List::AllUtils qw(uniq); use Path::Tiny; use namespace::autoclean; has include_untracked => ( is => 'ro', isa => 'Bool', default => 0, ); around dump_config => sub { my $orig = shift; my $self = shift; my $config = $self->$orig; $config->{+__PACKAGE__} = { include_untracked => $self->include_untracked, }; return $config; }; override gather_files => sub { my ($self) = @_; require Git::Wrapper; my $root = "" . $self->root; $root =~ s{^~([\\/])}{require File::HomeDir; File::HomeDir->my_home . $1}e; $root = Path::Tiny::path($root); my $git = Git::Wrapper->new("$root"); my @opts; @opts = qw(--cached --others --exclude-standard) if $self->include_untracked; my $exclude_regex = qr/\000/; $exclude_regex = qr/$exclude_regex|$_/ for ($self->exclude_match->flatten); my %is_excluded = map {; $_ => 1 } $self->exclude_filename->flatten; my @files; FILE: for my $filename (uniq $git->ls_files(@opts)) { my $file = Path::Tiny::path($filename)->relative($root); unless ($self->include_dotfiles) { next FILE if $file->basename =~ qr/^\./; next FILE if grep { /^\.[^.]/ } split q{/}, $file->parent->stringify; } next if $file =~ $exclude_regex; next if $is_excluded{ $file }; if (-d $file) { $self->log("WARNING: $file is symlink to directory, skipping it"); next; } push @files, $self->_file_from_filename($filename); } for my $file (@files) { (my $newname = $file->name) =~ s{\A\Q$root\E[\\/]}{}g; $newname = File::Spec->catdir($self->prefix, $newname) if $self->prefix; $newname = Path::Tiny::path($newname)->stringify; $file->name($newname); $self->add_file($file); } return; }; __PACKAGE__->meta->make_immutable; no Moose; 1; __END__ =pod =encoding UTF-8 =head1 NAME Dist::Zilla::Plugin::Git::GatherDir - gather all tracked files in a Git working directory =head1 VERSION version 2.025 =head1 DESCRIPTION This is a trivial variant of the L plugin. It looks in the directory named in the L attribute and adds all the Git tracked files it finds there (as determined by C). If the root begins with a tilde, the tilde is replaced with the current user's home directory according to L. Most users just need: [Git::GatherDir] ...and this will pick up all tracked files from the current directory into the dist. You can use it multiple times, as you can any other plugin, by providing a plugin name. For example, if you want to include external specification files into a subdir of your dist, you might write: [Git::GatherDir] ; this plugin needs no config and gathers most of your files [Git::GatherDir / SpecFiles] ; this plugin gets all tracked files in the root dir and adds them under ./spec root = ~/projects/my-project/spec prefix = spec =head1 ATTRIBUTES =head2 root This is the directory in which to look for files. If not given, it defaults to the dist root -- generally, the place where your F or other configuration file is located. =head2 prefix This parameter can be set to gather all the files found under a common directory. See the L above for an example. =head2 include_dotfiles By default, files will not be included if they begin with a dot. This goes both for files and for directories relative to the C. In almost all cases, the default value (false) is correct. =head2 include_untracked By default, files not tracked by Git will not be gathered. If this is set to a true value, then untracked files not covered by a Git ignore pattern (i.e. those reported by C) are also gathered (and you'll probably want to use L to ensure all files are checked in before a release). C requires at least Git 1.5.4, but you should probably not use it if your Git is older than 1.6.5.2. Versions before that would not list files matched by your F<.gitignore>, even if they were already being tracked by Git (which means they will not be gathered, even though they should be). Whether that is a problem depends on the contents of your exclude files (including the global one, if any). =head2 follow_symlinks Git::GatherDir does not honor GatherDir's L option. While the attribute exists (because Git::GatherDir is a subclass), setting it has no effect. Directories that are symlinks will not be gathered. Instead, you'll get a message saying C. To suppress the warning, add that directory to C or C. To gather the files in the symlinked directory, use a second instance of GatherDir or Git::GatherDir with appropriate C and C options. Files which are symlinks are always gathered. =head2 exclude_filename To exclude certain files from being gathered, use the C option. This may be used multiple times to specify multiple files to exclude. =head2 exclude_match This is just like C but provides a regular expression pattern. Files matching the pattern are not gathered. This may be used multiple times to specify multiple patterns to exclude. =for Pod::Coverage gather_dir gather_files =head1 AUTHOR Jerome Quelin =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2009 by Jerome Quelin. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Dist-Zilla-Plugin-Git-2.025/lib/Dist/Zilla/Plugin/Git/Commit.pm0000644000175000017500000001664412416261571022072 0ustar cjmcjm# # This file is part of Dist-Zilla-Plugin-Git # # This software is copyright (c) 2009 by Jerome Quelin. # # This is free software; you can redistribute it and/or modify it under # the same terms as the Perl 5 programming language system itself. # use 5.008; use strict; use warnings; package Dist::Zilla::Plugin::Git::Commit; { $Dist::Zilla::Plugin::Git::Commit::VERSION = '2.025'; } # ABSTRACT: commit dirty files use namespace::autoclean; use File::Temp qw{ tempfile }; use List::Util qw{ first }; use Moose; use MooseX::Has::Sugar; use MooseX::Types::Moose qw{ Str }; use MooseX::Types::Path::Tiny 0.010 qw{ Paths }; use Path::Tiny 0.048 qw(); # subsumes use Cwd; use String::Formatter method_stringf => { -as => '_format_string', codes => { c => sub { $_[0]->_get_changes }, d => sub { require DateTime; DateTime->now(time_zone => $_[0]->time_zone) ->format_cldr($_[1] || 'dd-MMM-yyyy') }, n => sub { "\n" }, N => sub { $_[0]->zilla->name }, t => sub { $_[0]->zilla->is_trial ? (defined $_[1] ? $_[1] : '-TRIAL') : '' }, v => sub { $_[0]->zilla->version }, }, }; with 'Dist::Zilla::Role::AfterRelease'; with 'Dist::Zilla::Role::Git::Repo'; with 'Dist::Zilla::Role::Git::DirtyFiles'; with 'Dist::Zilla::Role::GitConfig'; sub _git_config_mapping { +{ changelog => '%{changelog}s', } } # -- attributes has commit_msg => ( ro, isa=>Str, default => 'v%v%n%n%c' ); has time_zone => ( ro, isa=>Str, default => 'local' ); has add_files_in => ( ro, isa=> Paths, coerce => 1, default => sub { [] }); # -- public methods sub mvp_multivalue_args { qw( add_files_in ) } around dump_config => sub { my $orig = shift; my $self = shift; my $config = $self->$orig; $config->{+__PACKAGE__} = { map { $_ => $self->$_ } qw(commit_msg time_zone add_files_in), }; return $config; }; sub after_release { my $self = shift; my $git = $self->git; my @output; # check if there are dirty files that need to be committed. # at this time, we know that only those 2 files may remain modified, # otherwise before_release would have failed, ending the release # process. @output = sort { lc $a cmp lc $b } $self->list_dirty_files($git, 1); # add any other untracked files to the commit list if ( @{ $self->add_files_in } ) { my @untracked_files = $git->ls_files( { others=>1, 'exclude-standard'=>1 } ); foreach my $f ( @untracked_files ) { foreach my $path ( @{ $self->add_files_in } ) { if ( Path::Tiny::path( $path )->subsumes( $f ) ) { push( @output, $f ); last; } } } } # if nothing to commit, we're done! return unless @output; # write commit message in a temp file my ($fh, $filename) = tempfile( getcwd . '/DZP-git.XXXX', UNLINK => 1 ); binmode $fh, ':utf8' unless Dist::Zilla->VERSION < 5; print $fh $self->get_commit_message; close $fh; # commit the files in git $git->add( @output ); $self->log_debug($_) for $git->commit( { file=>$filename } ); $self->log("Committed @output"); } sub get_commit_message { my $self = shift; return _format_string($self->commit_msg, $self); } # end get_commit_message # -- private methods sub _get_changes { my $self = shift; # parse changelog to find commit message my $cl_name = $self->changelog; my $changelog = first { $_->name eq $cl_name } @{ $self->zilla->files }; unless ($changelog) { $self->log("WARNING: Unable to find $cl_name"); return ''; } my $newver = $self->zilla->version; $changelog->content =~ / ^\Q$newver\E(?![_.]*[0-9]).*\n # from line beginning with version number ( (?: (?> .* ) (?:\n|\z) )*? ) # capture as few lines as possible (?: (?> \s* ) ^\S | \z ) # until non-indented line or EOF /xm or do { $self->log("WARNING: Unable to find $newver in $cl_name"); return ''; }; (my $changes = $1) =~ s/^\s*\n//; # Remove leading blank lines $self->log("WARNING: No changes listed under $newver in $cl_name") unless length $changes; # return commit message return $changes; } # end _get_changes 1; __END__ =pod =encoding UTF-8 =head1 NAME Dist::Zilla::Plugin::Git::Commit - commit dirty files =head1 VERSION version 2.025 =head1 SYNOPSIS In your F: [Git::Commit] changelog = Changes ; this is the default =head1 DESCRIPTION Once the release is done, this plugin will record this fact in git by committing changelog and F. The commit message will be taken from the changelog for this release. It will include lines between the current version and timestamp and the next non-indented line, except that blank lines at the beginning or end are removed. B If you are using Git::Commit in conjunction with the L plugin, C<[NextRelease]> must come before C<[Git::Commit]> (or C<[@Git]>) in your F or plugin bundle. Otherwise, Git::Commit will commit the F file before NextRelease has updated it. The plugin accepts the following options: =over 4 =item * changelog - the name of your changelog file. Defaults to F. =item * allow_dirty - a file that will be checked in if it is locally modified. This option may appear multiple times. The default list is F and the changelog file given by C. =item * allow_dirty_match - works the same as allow_dirty, but matching as a regular expression instead of an exact filename. =item * add_files_in - a path that will have its new files checked in. This option may appear multiple times. This is used to add files generated during build-time to the repository, for example. The default list is empty. Note: The files have to be generated between those phases: BeforeRelease E-E AfterRelease, and after Git::Check + before Git::Commit. =item * commit_msg - the commit message to use. Defaults to C, meaning the version number and the list of changes. =item * time_zone - the time zone to use with C<%d>. Can be any time zone name accepted by DateTime. Defaults to C. =back You can use the following codes in commit_msg: =over 4 =item C<%c> The list of changes in the just-released version (read from C). It will include lines between the current version and timestamp and the next non-indented line, except that blank lines at the beginning or end are removed. It normally ends in a newline. =item C<%{dd-MMM-yyyy}d> The current date. You can use any CLDR format supported by L. A bare C<%d> means C<%{dd-MMM-yyyy}d>. =item C<%n> a newline =item C<%N> the distribution name =item C<%{-TRIAL}t> Expands to -TRIAL (or any other supplied string) if this is a trial release, or the empty string if not. A bare C<%t> means C<%{-TRIAL}t>. =item C<%v> the distribution version =back =head1 METHODS =head2 get_commit_message This method returns the commit message. The default implementation reads the Changes file to get the list of changes in the just-released version. =for Pod::Coverage after_release mvp_multivalue_args =head1 AUTHOR Jerome Quelin =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2009 by Jerome Quelin. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Dist-Zilla-Plugin-Git-2.025/lib/Dist/Zilla/Plugin/Git/Check.pm0000644000175000017500000001257212416261571021653 0ustar cjmcjm# # This file is part of Dist-Zilla-Plugin-Git # # This software is copyright (c) 2009 by Jerome Quelin. # # This is free software; you can redistribute it and/or modify it under # the same terms as the Perl 5 programming language system itself. # use 5.008; use strict; use warnings; package Dist::Zilla::Plugin::Git::Check; { $Dist::Zilla::Plugin::Git::Check::VERSION = '2.025'; } # ABSTRACT: check your git repository before releasing use Moose; use namespace::autoclean 0.09; use Moose::Util::TypeConstraints qw(enum); use constant _DieWarnIgnore => do { enum [qw[ die warn ignore ]] }; with 'Dist::Zilla::Role::AfterBuild'; with 'Dist::Zilla::Role::BeforeRelease'; with 'Dist::Zilla::Role::Git::Repo'; with 'Dist::Zilla::Role::Git::DirtyFiles'; with 'Dist::Zilla::Role::GitConfig'; has build_warnings => ( is=>'ro', isa => 'Bool', default => 0 ); has untracked_files => ( is=>'ro', isa => _DieWarnIgnore, default => 'die' ); sub _git_config_mapping { +{ changelog => '%{changelog}s', } } # -- public methods around dump_config => sub { my $orig = shift; my $self = shift; my $config = $self->$orig; $config->{+__PACKAGE__} = { untracked_files => $self->untracked_files, }; return $config; }; sub _perform_checks { my ($self, $log_method) = @_; my @issues; my $git = $self->git; my @output; # fetch current branch my ($branch) = map { /^\*\s+(.+)/ ? $1 : () } $git->branch; # check if some changes are staged for commit @output = $git->diff( { cached=>1, 'name-status'=>1 } ); if ( @output ) { push @issues, @output . " staged change" . (@output == 1 ? '' : 's'); my $errmsg = "branch $branch has some changes staged for commit:\n" . join "\n", map { "\t$_" } @output; $self->$log_method($errmsg); } # everything but files listed in allow_dirty should be in a # clean state @output = $self->list_dirty_files($git); if ( @output ) { push @issues, @output . " uncommitted file" . (@output == 1 ? '' : 's'); my $errmsg = "branch $branch has some uncommitted files:\n" . join "\n", map { "\t$_" } @output; $self->$log_method($errmsg); } # no files should be untracked @output = $git->ls_files( { others=>1, 'exclude-standard'=>1 } ); if ( @output ) { push @issues, @output . " untracked file" . (@output == 1 ? '' : 's'); my $untracked = $self->untracked_files; if ($untracked ne 'ignore') { # If $log_method is log_fatal, switch to log unless # untracked files are fatal. If $log_method is already log, # this is a no-op. $log_method = 'log' unless $untracked eq 'die'; my $errmsg = "branch $branch has some untracked files:\n" . join "\n", map { "\t$_" } @output; $self->$log_method($errmsg); } } if (@issues) { $self->log( "branch $branch has " . join(', ', @issues)); } else { $self->log( "branch $branch is in a clean state" ); } } # end _perform_checks sub after_build { my $self = shift; $self->_perform_checks('log') if $self->build_warnings; } sub before_release { my $self = shift; $self->_perform_checks('log_fatal'); } 1; __END__ =pod =encoding UTF-8 =head1 NAME Dist::Zilla::Plugin::Git::Check - check your git repository before releasing =head1 VERSION version 2.025 =head1 SYNOPSIS In your F: [Git::Check] allow_dirty = dist.ini allow_dirty = README changelog = Changes ; this is the default build_warnings = 0 ; this is the default untracked_files = die ; default value (can also be "warn" or "ignore") =head1 DESCRIPTION This plugin checks that git is in a clean state before releasing. The following checks are performed before releasing: =over 4 =item * there should be no files in the index (staged copy) =item * there should be no untracked files in the working copy =item * the working copy should be clean. The files listed in C can be modified locally, though. =back If those conditions are not met, the plugin will die, and the release will thus be aborted. This lets you fix the problems before continuing. The plugin accepts the following options: =over 4 =item * changelog - the name of your changelog file. defaults to F. =item * allow_dirty - a file that is allowed to have local modifications. This option may appear multiple times. The default list is F and the changelog file given by C. You can use C to prohibit all local modifications. =item * allow_dirty_match - works the same as allow_dirty, but matching as a regular expression instead of an exact filename. =item * build_warnings - if 1, perform the same checks after every build, but as warnings, not errors. Defaults to 0. =item * untracked_files - indicates what to do if there are untracked files. Must be either C (the default), C, or C. C lists the untracked files, while C only prints the total number of untracked files. =back =for Pod::Coverage after_build before_release =head1 AUTHOR Jerome Quelin =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2009 by Jerome Quelin. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Dist-Zilla-Plugin-Git-2.025/lib/Dist/Zilla/Plugin/Git/Push.pm0000644000175000017500000000713512416261571021554 0ustar cjmcjm# # This file is part of Dist-Zilla-Plugin-Git # # This software is copyright (c) 2009 by Jerome Quelin. # # This is free software; you can redistribute it and/or modify it under # the same terms as the Perl 5 programming language system itself. # use 5.008; use strict; use warnings; package Dist::Zilla::Plugin::Git::Push; { $Dist::Zilla::Plugin::Git::Push::VERSION = '2.025'; } # ABSTRACT: push current branch use Moose; use MooseX::Has::Sugar; use MooseX::Types::Moose qw{ ArrayRef Str }; use namespace::autoclean; with 'Dist::Zilla::Role::BeforeRelease'; with 'Dist::Zilla::Role::AfterRelease'; with 'Dist::Zilla::Role::Git::Repo'; with 'Dist::Zilla::Role::GitConfig'; sub mvp_multivalue_args { qw(push_to) } sub _git_config_mapping { +{ push_to => '%{remote}s %{local_branch}s:%{remote_branch}s', } } # -- attributes has remotes_must_exist => ( ro, isa=>'Bool', default=>1 ); has push_to => ( is => 'ro', isa => 'ArrayRef[Str]', lazy => 1, default => sub { [ qw(origin) ] }, ); around dump_config => sub { my $orig = shift; my $self = shift; my $config = $self->$orig; $config->{+__PACKAGE__} = { map { $_ => $self->$_ } qw(push_to remotes_must_exist), }; return $config; }; sub before_release { my $self = shift; return unless $self->remotes_must_exist; my %valid_remote = map { $_ => 1 } $self->git->remote; my @bad_remotes; # Make sure the remotes we'll be pushing to exist for my $remote_spec ( @{ $self->push_to } ) { (my $remote = $remote_spec) =~ s/\s.*//s; # Discard branch (if specified) if ($remote =~ m![:/]!) { # Appears to be a URL or path, don't check it $self->log("Will push to $remote (not checked)"); } else { # Named remotes must exist push @bad_remotes, $remote unless $valid_remote{$remote}; } } $self->log_fatal("These remotes do not exist: @bad_remotes") if @bad_remotes; } sub after_release { my $self = shift; my $git = $self->git; # push everything on remote branch for my $remote ( @{ $self->push_to } ) { $self->log("pushing to $remote"); my @remote = split(/\s+/,$remote); $self->log_debug($_) for $git->push( @remote ); $self->log_debug($_) for $git->push( { tags=>1 }, $remote[0] ); } } 1; __END__ =pod =encoding UTF-8 =head1 NAME Dist::Zilla::Plugin::Git::Push - push current branch =head1 VERSION version 2.025 =head1 SYNOPSIS In your F: [Git::Push] push_to = origin ; this is the default push_to = origin HEAD:refs/heads/released ; also push to released branch remotes_must_exist = 1 ; this is the default =head1 DESCRIPTION Once the release is done, this plugin will push current git branch to remote end, with the associated tags. The plugin accepts the following options: =over 4 =item * push_to - the name of the a remote to push to. The default is F. This may be specified multiple times to push to multiple repositories. =item * remotes_must_exist - if true, then Git::Push checks before a release to ensure that all named remotes specified in C are configured in your repo. The default is true. Remotes specified as a URL or path are not checked, but will produce a C message. =back =for Pod::Coverage after_release before_release mvp_multivalue_args =head1 AUTHOR Jerome Quelin =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2009 by Jerome Quelin. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Dist-Zilla-Plugin-Git-2.025/lib/Dist/Zilla/Plugin/Git/Init.pm0000644000175000017500000001100712416261571021531 0ustar cjmcjm# # This file is part of Dist-Zilla-Plugin-Git # # This software is copyright (c) 2009 by Jerome Quelin. # # This is free software; you can redistribute it and/or modify it under # the same terms as the Perl 5 programming language system itself. # use 5.010; use strict; use warnings; package Dist::Zilla::Plugin::Git::Init; { $Dist::Zilla::Plugin::Git::Init::VERSION = '2.025'; } # ABSTRACT: initialize git repository on dzil new our %transform = ( lc => sub { lc shift }, uc => sub { uc shift }, '' => sub { shift }, ); use Moose; use Git::Wrapper; use String::Formatter method_stringf => { -as => '_format_string', codes => { n => sub { "\n" }, N => sub { $transform{$_[1] || ''}->( $_[0]->zilla->name ) }, }, }; with 'Dist::Zilla::Role::AfterMint'; has commit_message => ( is => 'ro', isa => 'Str', default => 'initial commit', ); has commit => ( is => 'ro', isa => 'Bool', default => 1, ); has branch => ( is => 'ro', isa => 'Str', default => '', ); has remotes => ( is => 'ro', isa => 'ArrayRef[Str]', default => sub { [] }, ); has config_entries => ( is => 'ro', isa => 'ArrayRef[Str]', default => sub { [] }, ); sub mvp_multivalue_args { qw(config_entries remotes) } sub mvp_aliases { return { config => 'config_entries', remote => 'remotes' } } sub after_mint { my $self = shift; my ($opts) = @_; my $git = Git::Wrapper->new("$opts->{mint_root}"); $self->log("Initializing a new git repository in " . $opts->{mint_root}); $git->init; foreach my $configSpec (@{ $self->config_entries }) { my ($option, $value) = split ' ', _format_string($configSpec, $self), 2; $self->log_debug("Configuring $option $value"); $git->config($option, $value); } $git->add("$opts->{mint_root}"); if ($self->commit) { my $message = 'Made initial commit'; if (length $self->branch) { $git->checkout('-b', $self->branch); $message .= ' on branch ' . $self->branch; } $git->commit({message => _format_string($self->commit_message, $self)}); $self->log($message); } foreach my $remoteSpec (@{ $self->remotes }) { my ($remote, $url) = split ' ', _format_string($remoteSpec, $self), 2; $self->log_debug("Adding remote $remote as $url"); $git->remote(add => $remote, $url); } } 1; __END__ =pod =encoding UTF-8 =head1 NAME Dist::Zilla::Plugin::Git::Init - initialize git repository on dzil new =head1 VERSION version 2.025 =head1 SYNOPSIS In your F: [Git::Init] commit_message = initial commit ; this is the default commit = 1 ; this is the default branch = ; this is the default (means master) remote = origin git@github.com:USERNAME/%{lc}N.git ; no default config = user.email USERID@cpan.org ; there is no default =head1 DESCRIPTION This plugin initializes a git repository when a new distribution is created with C. =head2 Plugin options The plugin accepts the following options: =over 4 =item * commit_message - the commit message to use when checking in the newly-minted dist. Defaults to C. =item * commit - if true (the default), commit the newly-minted dist. If set to a false value, add the files to the Git index but don't actually make a commit. =item * branch - the branch name under which the newly-minted dist is checked in (if C is true). Defaults to an empty string, which means that the Git default branch is used (master). =item * config - a config setting to make in the repository. No config entries are made by default. A setting is specified as C