Dist-Starter

 view release on metacpan or  search on metacpan

lib/Dist/Starter.pm  view on Meta::CPAN

use Try::Tiny qw( try catch );

use Dist::Starter::Context   ();
use Dist::Starter::Exception ();

sub DISTNAME_PLACEHOLDER () { '{{distname}}' }

sub croakf ( $@ ) {
  require Carp;
  @_ = ( ( @_ == 1 ? shift : sprintf shift, @_ ) . ', stopped' );
  goto &Carp::croak;
}

sub print_usage_info () {
  my $script_name = basename( $0 );
  print STDOUT "Usage: $script_name [ -V | -h  ]\n",
"       $script_name [ -T <template> ] [ -o <output directory> ] [ -n ] [ -A <abstract> ] [ -G <git base url> ] [ -I <initial version> ] [ -L <license> ] [ -M <min perl version> ] [ -S <share property>=<share value> ] [ -a <author from> ] <distname>\...
  EOOD
}

sub resolve_existing_project_conflict ( $$$ ) {
  my ( $before_file, $project, $conflict_resolution ) = @_;

  # Possible conflict resolution enum values:
  # undef => fail
  # ????? => overwrite if file exists
  # ????? => skip if file exists
  unless ( $conflict_resolution ) {
    remove_tree( $before_file );
    die Dist::Starter::Exception->new( message => "Project directory '$project' already exists" )
  }

  undef
}

sub scaffold ( $$$ ) {
  my ( $entry_point, $output_directory, $context ) = @_;

  # This isn't the final project name. The final project name will be created
  # by the last element
  # ...
  # unless ( move $before_file, $after_file ) {
  # ...
  # in the below foreach loop
  my $project =
    catdir( $output_directory eq 'TEMP_DIR' ? tempdir( CLEANUP => $ENV{ HARNESS_ACTIVE } // 0 ) : $output_directory,
    DISTNAME_PLACEHOLDER );

  dircopy( $entry_point, $project ) or croakf $!;

  my @files;
  my $wanted = sub {
    my $file = $_;
    push @files, $file;
    if ( -f $file ) {
      # Add write permission to the owner because dircopy() hasn't kept that
      chmod( ( stat( $file ) )[ 2 ] | S_IWUSR, $file );
      # Enable inplace-editing
      # https://stackoverflow.com/questions/31024980/perl-in-place-editing-within-a-script-rather-than-one-liner
      local $^I   = '';
      local @ARGV = ( $file );
      while ( <ARGV> ) {
        # Skip lines that contain any number (even 0) of blank characters after placeholder substitution
        next if s/\{\{ ( [^}]+ ) \}\}/$context->lookup( $1 )/xeg and m/\A[ \t]*\R\z/;
        print;
      }
    }
  };

  # Let $wanted apply in-place edits to the content of each regular file
  find( { wanted => $wanted, no_chdir => 1, bydepth => 1 }, $project );

  my $distname = $context->lookup( 'distname' );
  defined(
    $project = try {
      # Now its time to look for placeholders in the file names
      # On purpose declare $after_file before the foreach loop
      my $after_file;
      foreach my $before_file ( @files ) {
        ( $after_file = $before_file ) =~ s/\{\{ ( [^}]+ ) \}\} \z/$context->lookup( $1 )/xe;
        if ( $after_file ne $before_file ) {
          make_path dirname $after_file;
          unless ( move $before_file, $after_file ) {
            croakf "Cannot move '%s' to '%s': %s", $before_file, $after_file, $!
              unless $after_file =~ m/$distname\z/;
            resolve_existing_project_conflict $before_file, $after_file, my $conflict_resolution;
          }
        }
      }
      # The last value of $after_file is the actual $project
      $after_file
    } catch {
      $_->$_isa( 'Dist::Starter::Exception' ) ? say STDERR $_->message : die $_;
      undef
    }
  ) or return EXIT_USAGE;
  # https://cookiecutter.readthedocs.io/en/latest/advanced/replay.html
  # https://github.com/cookiecutter/cookiecutter/issues/104
  # TODO:
  # The name of the replay file is wrong because it does not contain the name
  # of the template (basename( $template )). Furthermore it should be
  # discussed if the replay files should be stored at a central place or local
  # to the new project
  # $context->dump_to_file( catfile( $after_file, '.' . basename( $0 ) . '_replay.yml' ) );
  say STDOUT $project;

  EXIT_SUCCESS
}

sub run {
  shift;
  my @argv = @_ ? @_ : @ARGV;

  # CLI step
  # Set defaults
  unshift @argv, '-T', catdir( dist_dir( 'Dist-Starter' ), qw( templates perl-dist-eummcpf ) );
  unshift @argv, '-o', rel2abs curdir;
  readopts @argv;

  # Parse command-line
  my $template;



( run in 1.105 second using v1.01-cache-2.11-cpan-a49fcb8fa48 )