diff --git a/Mojolicious-Command-deploy-heroku-0.12.tar.gz b/Mojolicious-Command-deploy-heroku-0.12.tar.gz deleted file mode 100644 index fed1edf..0000000 Binary files a/Mojolicious-Command-deploy-heroku-0.12.tar.gz and /dev/null differ diff --git a/Mojolicious-Command-deploy-heroku-0.24.tar.gz b/Mojolicious-Command-deploy-heroku-0.24.tar.gz new file mode 100644 index 0000000..1ce5ecd Binary files /dev/null and b/Mojolicious-Command-deploy-heroku-0.24.tar.gz differ diff --git a/README.pod b/README.pod index 1c25630..e138857 100644 --- a/README.pod +++ b/README.pod @@ -4,7 +4,7 @@ Mojolicious::Command::deploy::heroku - Deploy to Heroku -=head1 USAGE +=head1 SYNOPSIS script/my_app deploy heroku [OPTIONS] @@ -18,10 +18,9 @@ Mojolicious::Command::deploy::heroku - Deploy to Heroku script/my_app deploy heroku --name happy-cloud-1234 These options are available: - -n, --appname Specify app for deployment - -a, --api-key Heroku API key (read from ~/.heroku/credentials by default). - -c, --create Create a new Heroku app - -v, --verbose Verbose output (heroku response, git output) + -n, --name Specify app for deployment + -a, --api-key Heroku API key (read from ~/.netrc by default) + -c, --create [name] Create a new Heroku app with an optional name -h, --help This message =head1 DESCRIPTION @@ -30,7 +29,7 @@ L deploys a Mojolicious app to Heroku. *NOTE* The deploy command itself works on Windows, but the Heroku service does not reliably accept deployments from Windows. Your mileage may vary. -*NOTE* This release works with Mojolicious versions 4.50 and above. For older Mojolicious versions, please use 0.10 or before. +*NOTE* This release works with Mojolicious versions 7.20 and above. For older Mojolicious versions, please use 0.13 or before. =head1 WORKFLOW @@ -38,7 +37,7 @@ L deploys a Mojolicious app to Heroku. =item 1) B -L +L =item 2) B @@ -46,18 +45,25 @@ L =item 3) B - hello deploy heroku --create + ./hello deploy heroku --create [optional-name] The deploy command creates a git repository of the B in /tmp, and then pushes it to a remote heroku repository. +For applications with additional dependencies, you will need to generate a Makefile.PL and manually add your dependencies to the "PREREQ_PM" section before deploying: + + ./hello generate makefile + =back =head1 SEE ALSO -L, -L, -L, -L +L + +L + +L + +L =head1 SOURCE @@ -65,7 +71,7 @@ L =head1 VERSION -0.22 +0.24 =head1 AUTHOR @@ -77,4 +83,6 @@ MattOates briandfoy +rage311 + =cut diff --git a/dist.ini b/dist.ini index c1793a4..88b0c58 100644 --- a/dist.ini +++ b/dist.ini @@ -2,20 +2,18 @@ name = Mojolicious-Command-deploy-heroku auth = Glen Hinkle license = Perl_5 copyright_holder = Glen Hinkle -version = 0.12 +version = 0.24 [@Filter] bundle = @Basic ;remove = Readme [Prereqs] -File::Path = 2.08 -File::Slurp = 9999.19 IPC::Cmd = 0.78 -Mojolicious = 4.82 +Mojolicious = 8.0 Mojolicious::Command::deploy = 0.01 -Net::Heroku = 0.08 -;IO::All = 0.44 +Net::Heroku = 0.20 +Net::Netrc = 3.10 [MetaResources] repository.web = https://github.com/tempire/mojolicious-command-deploy-heroku diff --git a/lib/Mojolicious/Command/generate/heroku.pm b/lib/Mojolicious/Command/Author/generate/heroku.pm similarity index 62% rename from lib/Mojolicious/Command/generate/heroku.pm rename to lib/Mojolicious/Command/Author/generate/heroku.pm index 1b459fe..508b4b1 100644 --- a/lib/Mojolicious/Command/generate/heroku.pm +++ b/lib/Mojolicious/Command/Author/generate/heroku.pm @@ -1,4 +1,4 @@ -package Mojolicious::Command::generate::heroku; +package Mojolicious::Command::Author::generate::heroku; use Mojo::Base 'Mojolicious::Command'; use Mojo::Util 'class_to_file'; @@ -15,7 +15,9 @@ sub run { ? $0 : 'script/' . class_to_file($class); - $self->render_to_rel_file(perloku => $self->file => $script_name); + $self->render_to_rel_file( + perloku => $self->file => { script_name => $script_name } + ); $self->chmod_file($self->file => 0744); } @@ -23,28 +25,27 @@ sub run { __DATA__ @@ perloku -#!/bin/sh -./<%= +(shift =~ qr|[\./]*(.+)|)[0] %> daemon --listen http://*:$PORT --mode production +web: ./<%= +($script_name =~ qr|[\./]*(.+)|)[0] %> daemon --listen http://*:$PORT --mode production __END__ =head1 NAME -Mojolicious::Command::generate::heroku - Heroku configuration generator command +Mojolicious::Command::Author::generate::heroku - Heroku configuration generator command =head1 SYNOPSIS - use Mojolicious::Command::generate::heroku; + use Mojolicious::Command::Author::generate::heroku; - my $heroku = Mojolicious::Command::generate::heroku->new; + my $heroku = Mojolicious::Command::Author::generate::heroku->new; $heroku->run(@ARGV); =head1 DESCRIPTION -L is a heroku configuration generator. +L is a heroku configuration generator. =head1 ATTRIBUTES -L inherits all attributes from +L inherits all attributes from L and implements the following new ones. =head2 C @@ -63,7 +64,7 @@ Usage information for this command, used for the help screen. =head1 METHODS -L inherits all methods from +L inherits all methods from L and implements the following new ones. =head2 C diff --git a/lib/Mojolicious/Command/deploy/heroku.pm b/lib/Mojolicious/Command/deploy/heroku.pm index 3dd86f0..9e988f7 100644 --- a/lib/Mojolicious/Command/deploy/heroku.pm +++ b/lib/Mojolicious/Command/deploy/heroku.pm @@ -1,68 +1,55 @@ package Mojolicious::Command::deploy::heroku; use Mojo::Base 'Mojolicious::Command'; -#use IO::All 'io'; use IO::Prompter; -use File::Path 'make_path'; -use File::Slurp qw/ slurp write_file /; use File::Spec; -use Getopt::Long qw/ GetOptions :config no_auto_abbrev no_ignore_case /; +use Mojo::File; use IPC::Cmd 'can_run'; +use Net::Netrc; use Mojo::IOLoop; use Mojo::UserAgent; -use Mojolicious::Command::generate::heroku; -use Mojolicious::Command::generate::makefile; +use Mojolicious::Command::Author::generate::heroku; +use Mojolicious::Command::Author::generate::makefile; use Net::Heroku; -our $VERSION = 0.12; +our $VERSION = 0.24; -has tmpdir => sub { $ENV{MOJO_TMPDIR} || File::Spec->tmpdir }; -has ua => sub { Mojo::UserAgent->new->ioloop(Mojo::IOLoop->singleton) }; -has description => "Deploy Mojolicious app to Heroku.\n"; +has tmpdir => sub { $ENV{MOJO_TMPDIR} || File::Spec->tmpdir }; +has ua => sub { Mojo::UserAgent->new->ioloop(Mojo::IOLoop->singleton) }; +has description => "Deploy Mojolicious app to Heroku\n"; has opt => sub { {} }; -has credentials_file => sub {"$ENV{HOME}/.heroku/credentials"}; +has credentials_file => sub { "$ENV{HOME}/.netrc" }; has makefile => 'Makefile.PL'; -has usage => <<"EOF"; +has usage => sub { shift->extract_usage }; -usage: $0 deploy heroku [OPTIONS] - - # Create new app with randomly selected name and deploy - $0 deploy heroku -c - - # Deploy to specified app and deploy (creates app if it does not exist) - $0 deploy heroku -n friggin-ponycorns - -These options are available: - -n, --appname Specify app name for deployment - -a, --api-key Heroku API key (read from ~/.heroku/credentials by default). - -c, --create Create app with randomly selected name - -v, --verbose Verbose output (heroku response, git output) - -h, --help This message -EOF sub opt_spec { my $self = shift; my $opt = {}; - return $opt - if GetOptions( - "appname|n=s" => sub { $opt->{name} = pop }, - "api-key|a=s" => sub { $opt->{api_key} = pop }, - "create|c" => sub { $opt->{create} = pop }, - ); + Mojo::Util::getopt( + 'name|n=s' => \$opt->{name}, + 'api-key|a=s' => \$opt->{api_key}, + 'create|c:s' => sub { $opt->{create} = $_[1] ? ($opt->{name} = $_[1]) : 1 }, + #'verbose|v' => \$opt->{verbose}, + ); + + return $opt; } sub validate { my $self = shift; my $opt = shift; - my @errors = - map $_ . ' command not found' => - grep !can_run($_) => qw/ git ssh ssh-keygen /; + my @errors = map { + $_ . ' command not found' + } grep { + !can_run($_) + } qw/ git ssh ssh-keygen /; # Create or appname - push @errors => '--create or --appname must be specified' - if !defined $opt->{create} and !defined $opt->{name}; + push @errors => '--create or --name must be specified' + if !defined $opt->{create} && !defined $opt->{name}; return @errors; } @@ -90,7 +77,7 @@ sub run { # SSH key permissions if (!remote_key_match($h)) { - print "\nHeroku does not have any SSH keys stored for you."; + print "\nHeroku does not have any matching SSH keys stored for you."; my ($file, $key) = create_or_get_key(); print "\nUploading SSH public key $file\n"; @@ -103,7 +90,7 @@ sub run { config_app( $h, create_or_get_app($h, $opt), - {BUILDPACK_URL => 'http://github.com/tempire/perloku.git'} + { BUILDPACK_URL => 'http://github.com/rage311/perloku.git' } ) ); @@ -115,7 +102,7 @@ sub run { push_repo( fill_repo( $self->create_repo($home_dir, $self->tmpdir), - $self->app->home->list_files + $self->app->home->list ), $res ); @@ -152,67 +139,54 @@ sub choose_key { sub generate_key { print "\nGenerating an SSH public key...\n"; - my $file = "id_rsa"; - - # Get/create dir - #my $dir = io->dir("$ENV{HOME}/.ssh")->perms(0700)->mkdir; - my $dir = File::Spec->catfile($ENV{HOME}, '.ssh'); - make_path($dir, {mode => 0700}); - # Generate RSA key - my $path = File::Spec->catfile($dir, $file); - `ssh-keygen -t rsa -N "" -f "$path" 2>&1`; + my $path = Mojo::File->new($ENV{HOME}, '.ssh') + ->make_path({mode => 0700 }) + ->child('id_rsa_test'); + + my $exit = system('ssh-keygen', '-t', 'rsa', '-N', '', '-f', $path); - return "$path.pub"; + return $path . '.pub'; } sub ssh_keys { - - #return grep /\.pub$/ => io->dir("$ENV{HOME}/.ssh/")->all; - opendir(my $dir => File::Spec->catfile($ENV{HOME}, '.ssh')) or return; - return - map File::Spec->catfile($ENV{HOME}, '.ssh', $_) => - grep /\.pub$/ => readdir($dir); + return Mojo::File->new($ENV{HOME}, '.ssh')->list->grep(qr/\.pub$/)->each; } sub create_or_get_key { - - #return io->file(ssh_keys() ? choose_key : generate_key)->slurp; - my $file = ssh_keys() ? choose_key : generate_key; - return $file, slurp $file; + my $file = Mojo::File->new(ssh_keys() ? choose_key : generate_key); + return $file, $file->slurp; } sub generate_makefile { my $self = shift; - my $command = Mojolicious::Command::generate::makefile->new; + my $command = Mojolicious::Command::Author::generate::makefile->new; my $file = $self->app->home->rel_file($self->makefile); if (!file_exists($file)) { - print "$file not found...generating\n"; + print "$file not found. Generating...\n"; return $command->run; } - unless ( `$^X -c $file` =~ /syntax OK/ ) { - die "$file does not compile. Cannot continue.\n"; - } + die "$file does not compile. Cannot continue." + unless (qx/$^X -c $file 2>&1/ =~ /syntax OK/); } sub generate_herokufile { my $self = shift; - my $command = Mojolicious::Command::generate::heroku->new; + my $command = + Mojolicious::Command::Author::generate::heroku->new(app => $self->app); if (!file_exists($command->file)) { - print $command->file . " not found...generating\n"; + print $command->file . " not found. Generating...\n"; return $command->run; } } sub file_exists { - - #return io(shift)->exists; return -e shift; } @@ -241,22 +215,30 @@ sub heroku_object { sub save_local_api_key { my ($self, $email, $api_key) = @_; - #my $dir = io->dir("$ENV{HOME}/.heroku")->perms(0700)->mkdir; - my $dir = "$ENV{HOME}/.heroku"; - make_path($dir, {mode => 0700}); + my $path = Mojo::File->new($self->credentials_file); + my $exists = file_exists $path; + + $path->spurt( + -T $path ? $path->slurp : '', + "machine api.heroku.com\n", + " password $api_key\n", + " login $email\n", + "machine git.heroku.com\n", + " password $api_key\n", + " login $email\n", + ); + + chmod 0600, $path if !$exists; - #return io("$dir/credentials")->print($email, "\n", $api_key, "\n"); - return write_file "$dir/credentials", $email, "\n", $api_key, "\n"; + return $path; } sub local_api_key { my $self = shift; - return if !-T $self->credentials_file; + return if ! -T $self->credentials_file; - #my $api_key = +(io->file($self->credentials_file)->slurp)[-1]; - my $api_key = +(slurp $self->credentials_file)[-1]; - chomp $api_key; + my $api_key = Net::Netrc->lookup('api.heroku.com')->password; return $api_key; } @@ -269,7 +251,7 @@ sub prompt_user_pass { my $email = prompt('Email:', -stdio); chomp $email; - my $password = prompt('Password:', -echo=>'*', -stdio); + my $password = prompt('Password:', -echo => '*', -stdio); chomp $password; return (email => $email, password => $password); @@ -278,10 +260,10 @@ sub prompt_user_pass { sub create_repo { my ($self, $home_dir, $tmp_dir) = @_; - print "Creating git repo\n"; + print "\nCreating git repo\n"; my $git_dir = - File::Spec->catfile($tmp_dir, 'mojo_deploy_git', int rand 1000); - make_path($git_dir); + Mojo::File::tempdir($tmp_dir . '/mojo_deploy_git_XXXXXXXX')->make_path; + print "$git_dir\n\n"; my $r = { work_tree => $home_dir, @@ -300,9 +282,12 @@ sub fill_repo { my @ignore = git($r, 'ls-files' => '--others' => '-i' => '--exclude-standard'); - my @files = - grep { my $file = $_; $file if !grep $file =~ /$_\W*/ => @ignore } - @$all_files; + my @files = grep { + my $file = $_; + $file if !grep { + $file =~ /$_\W*/ + } @ignore + } @$all_files; # Add files filtered by .gitignore print "Adding file $_\n" for @files; @@ -327,9 +312,12 @@ sub push_repo { sub git { my $r = shift; my $cmd = - "git -c core.autocrlf=false --work-tree=\"$r->{work_tree}\" --git-dir=\"$r->{git_dir}\" " - . join " " => @_; - return `$cmd`; + 'git -c core.autocrlf=false ' + . "--work-tree=\"$r->{work_tree}\" " + . "--git-dir=\"$r->{git_dir}\" " + . join ' ' => @_; + + return wantarray ? qx{$cmd} : system($cmd); } sub create_or_get_app { @@ -337,11 +325,11 @@ sub create_or_get_app { # Attempt create my %params = defined $opt->{name} ? (name => $opt->{name}) : (); - my $res = {$h->create(%params)}; + my $res = { $h->create(%params) }; my $error = $h->error; # Attempt retrieval - $res = shift @{[grep $_->{name} eq $opt->{name} => $h->apps]} + $res = shift @{[ grep { $_->{name} eq $opt->{name} } $h->apps ]} if $h->error and $h->error eq 'Name is already taken'; print "Upload failed for $opt->{name}: " . $error . "\n" and exit if !$res; @@ -352,12 +340,13 @@ sub create_or_get_app { sub remote_key_match { my $h = pop; - my %remote_keys = map { $_->{contents} => $_->{email} } $h->keys; - my @local_keys = map substr(slurp($_), 0, -1) => ssh_keys(); + my %remote_keys = map { $_->{public_key} => $_->{email} } $h->keys; - #my @local_keys = map substr($_->all, 0, -1) => ssh_keys(); + my @local_keys = map { + substr(Mojo::File->new($_)->slurp, 0, -1) + } ssh_keys(); - return grep defined $remote_keys{$_} => @local_keys; + return grep { defined $remote_keys{$_} } @local_keys; } sub config_app { @@ -374,7 +363,6 @@ sub verify_app { my ($h, $res) = @_; # This is the way Heroku's official command-line client does it. - for (0 .. 5) { last if $h->app_created(name => $res->{name}); sleep 1; @@ -390,7 +378,7 @@ sub verify_app { Mojolicious::Command::deploy::heroku - Deploy to Heroku -=head1 USAGE +=head1 SYNOPSIS script/my_app deploy heroku [OPTIONS] @@ -404,10 +392,9 @@ Mojolicious::Command::deploy::heroku - Deploy to Heroku script/my_app deploy heroku --name happy-cloud-1234 These options are available: - -n, --appname Specify app for deployment - -a, --api-key Heroku API key (read from ~/.heroku/credentials by default). - -c, --create Create a new Heroku app - -v, --verbose Verbose output (heroku response, git output) + -n, --name Specify app for deployment + -a, --api-key Heroku API key (read from ~/.netrc by default) + -c, --create [name] Create a new Heroku app with an optional name -h, --help This message =head1 DESCRIPTION @@ -416,7 +403,7 @@ L deploys a Mojolicious app to Heroku. *NOTE* The deploy command itself works on Windows, but the Heroku service does not reliably accept deployments from Windows. Your mileage may vary. -*NOTE* This release works with Mojolicious versions 4.50 and above. For older Mojolicious versions, please use 0.10 or before. +*NOTE* This release works with Mojolicious versions 7.20 and above. For older Mojolicious versions, please use 0.13 or before. =head1 WORKFLOW @@ -424,7 +411,7 @@ L deploys a Mojolicious app to Heroku. =item 1) B -L +L =item 2) B @@ -432,18 +419,25 @@ L =item 3) B - hello deploy heroku --create + ./hello deploy heroku --create [optional-name] The deploy command creates a git repository of the B in /tmp, and then pushes it to a remote heroku repository. +For applications with additional dependencies, you will need to generate a Makefile.PL and manually add your dependencies to the "PREREQ_PM" section before deploying: + + ./hello generate makefile + =back =head1 SEE ALSO -L, -L, -L, -L +L + +L + +L + +L =head1 SOURCE @@ -451,7 +445,7 @@ L =head1 VERSION -0.22 +0.24 =head1 AUTHOR @@ -463,4 +457,6 @@ MattOates briandfoy +rage311 + =cut diff --git a/t/generate.t b/t/generate.t index 53e8d4f..c000b69 100644 --- a/t/generate.t +++ b/t/generate.t @@ -1,7 +1,7 @@ use Test::More; -use Mojolicious::Command::generate::heroku; +use Mojolicious::Command::Author::generate::heroku; -ok my $o = Mojolicious::Command::generate::heroku->new; +ok my $o = Mojolicious::Command::Author::generate::heroku->new; ok $o->can('run'); done_testing;