=head1 DESCRIPTION
-C<homebank2ledger> converts L<HomeBank|http://homebank.free.fr/> files to a format usable by
+F<homebank2ledger> converts L<HomeBank|http://homebank.free.fr/> files to a format usable by
L<Ledger|https://www.ledger-cli.org/>. It can also convert directly to the similar
L<Beancount|http://furius.ca/beancount/> format.
This software is B<EXPERIMENTAL>, in early development. Its interface may change without notice.
-I wrote C<homebank2ledger> because I have been maintaining my own personal finances using HomeBank
+I wrote F<homebank2ledger> because I have been maintaining my own personal finances using HomeBank
(which is awesome) and I wanted to investigate using plain text accounting programs. It works well
enough for my data, but you may be using HomeBank features that I don't so there may be cases this
doesn't handle well or at all. Feel free to file a bug report. This script does NOT try to modify
Or if you don't plan to switch completely off of HomeBank, you can continue to maintain your
accounts in HomeBank and use this script to also take advantage of the reports Ledger offers.
+=head1 INSTALL
+
+There are several ways to install F<homebank2ledger> to your system.
+
+=head2 using cpanm
+
+You can install F<homebank2ledger> using L<cpanm>. If you have a local perl (plenv, perlbrew, etc.),
+you can just do:
+
+ cpanm App::Homebank2Ledger
+
+to install the F<homebank2ledger> executable and its dependencies. The executable will be installed
+to your perl's bin path, like F<~/perl5/perlbrew/bin/homebank2ledger>.
+
+If you're installing to your system perl, you can do:
+
+ cpanm --sudo App::Homebank2Ledger
+
+to install the F<homebank2ledger> executable to a system directory, like
+F</usr/local/bin/homebank2ledger> (depending on your perl).
+
+=head2 Downloading just the executable
+
+You may also choose to download F<homebank2ledger> as a single executable, like this:
+
+ curl -OL https://raw.githubusercontent.com/chazmcgarvey/homebank2ledger/solo/homebank2ledger
+ chmod +x homebank2ledger
+
+=head2 For developers
+
+If you're a developer and want to hack on the source, clone the repository and pull the
+dependencies:
+
+ git clone https://github.com/chazmcgarvey/homebank2ledger.git
+ cd homebank2ledger
+ make bootstrap # installs dependencies; requires cpanm
+
=head1 OPTIONS
=head2 --version
=head2 --rename-account STR
-Specifies a mapping for renaming accounts in the output. By default C<homebank2ledger> tries to come
+Specifies a mapping for renaming accounts in the output. By default F<homebank2ledger> tries to come
up with sensible account names (based on your HomeBank accounts and categories) that fit into five
root accounts:
=cut
+# FATPACK - Do not remove this line.
+
use warnings;
use strict;
license = MIT
[@Filter]
--bundle = @Author::CCM
--remove = Test::CleanNamespaces
-max_target_perl = 5.14
+-bundle = @Author::CCM
+-remove = Test::CleanNamespaces
+max_target_perl = 5.14
+PruneFiles.filename = maint
+
+[Run::Release]
+run = %x maint%pbranch_solo.pl %v %d
[ConsistentVersionTest]
--- /dev/null
+#!/usr/bin/env perl
+
+# This script prepares the homebank2ledger script for standalone use and puts it in a new branch.
+
+use strict;
+use warnings;
+use autodie;
+
+use File::Copy;
+use File::Path qw(make_path remove_tree);
+use String::ShellQuote;
+
+
+my $version = shift or die 'Need version';
+my $distdir = shift or die 'Need distdir';
+
+my $branch_name = 'solo';
+my $script_name = 'homebank2ledger';
+
+my $branch_oldref = '';
+my $branch_oldref_origin = '';
+
+open(my $fh, '-|', qw{git show-ref}, $branch_name);
+while (my $line = <$fh>) {
+ chomp $line;
+ my ($hash, $ref) = split(/\s+/, $line);
+ $branch_oldref = $hash if $ref eq "refs/heads/$branch_name";
+ $branch_oldref_origin = $hash if $ref eq "refs/remotes/origin/$branch_name";
+}
+if ($branch_oldref_origin && $branch_oldref ne $branch_oldref_origin) {
+ # reset local branch
+ system(qw{git branch -f}, $branch_name, "origin/$branch_name");
+ $branch_oldref = $branch_oldref_origin
+}
+
+my $commit_msg = shell_quote("Release $version");
+
+my $solodir = "solo_branch.$$";
+make_path($solodir);
+
+use Config;
+system($Config{'perlpath'}, qw{maint/fatpack.pl}, $distdir);
+move($script_name, "$solodir/$script_name");
+
+copy("$distdir/README", "$solodir/README");
+
+system(qw{git update-index --add}, glob("$solodir/*"));
+my $tree_ref = `git write-tree --prefix=$solodir/`;
+chomp $tree_ref;
+
+system(qw{git reset});
+remove_tree($solodir);
+
+my $branch_oldref_safe = shell_quote($branch_oldref);
+my $tree_ref_safe = shell_quote($tree_ref);
+my $parent = $branch_oldref ? "-p $branch_oldref_safe" : '';
+my $commit_ref = `git commit-tree -m $commit_msg $parent $tree_ref_safe`;
+chomp $commit_ref;
+
+system(qw(git branch -f), $branch_name, $commit_ref);
+system(qw(git tag -a -m), "Version $version", "$branch_name-$version", $commit_ref);
+
--- /dev/null
+#!/usr/bin/env perl
+
+# This script creates a fatpacked version of homebank2ledger. Much of this code was inspired by or
+# blatantly copied from cpanminus build scripts, written by Tatsuhiko Miyagawa.
+
+use warnings FATAL => 'all';
+use strict;
+use autodie ':all';
+
+use App::FatPacker ();
+use Cwd;
+use File::Find;
+use File::Path;
+use File::pushd;
+use Module::CoreList;
+
+
+my $distdir = shift;
+
+my $script_name = 'bin/homebank2ledger';
+my $libdir = 'lib';
+
+if ($distdir) {
+ if (-d "$distdir/blib") {
+ $script_name = "$distdir/blib/script/homebank2ledger";
+ $libdir = "$distdir/blib/lib";
+ }
+ else {
+ $script_name = "$distdir/$script_name";
+ $libdir = "$distdir/$libdir";
+ }
+}
+
+make_fatlib();
+make_script();
+exit;
+
+
+BEGIN {
+ # IO::Socket::IP requires newer Socket, which is C-based
+ $ENV{PERL_HTTP_TINY_IPV4_ONLY} = 1;
+}
+
+END {
+ no autodie;
+ unlink('homebank2ledger.tmp');
+ rmtree('.fatpack-build');
+ rmtree('fatlib');
+}
+
+
+sub find_requires {
+ my $file = shift;
+
+ my %requires;
+ open my $in, "<", $file;
+ while (<$in>) {
+ /^\s*(?:use|require) (\S+)[^;]*;\s*$/
+ and $requires{$1} = 1;
+ }
+
+ keys %requires;
+}
+
+sub mod_to_pm {
+ local $_ = shift;
+ s!::!/!g;
+ "$_.pm";
+}
+
+sub pm_to_mod {
+ local $_ = shift;
+ s!/!::!g;
+ s/\.pm$//;
+ $_;
+}
+
+sub in_lib {
+ my $file = shift;
+ -e "$libdir/$file";
+}
+
+sub is_core {
+ my $module = shift;
+ exists $Module::CoreList::version{5.008001}{$module};
+}
+
+sub exclude_modules {
+ my($modules, $except) = @_;
+ my %exclude = map { $_ => 1 } @$except;
+ [ grep !$exclude{$_}, @$modules ];
+}
+
+sub pack_modules {
+ my($path, $modules, $no_trace) = @_;
+
+ $modules = exclude_modules($modules, $no_trace);
+
+ my $packer = App::FatPacker->new;
+ my @requires = grep !is_core(pm_to_mod($_)), grep /\.pm$/, split /\n/,
+ $packer->trace(use => $modules, args => ['-e', 1]);
+ push @requires, map mod_to_pm($_), @$no_trace;
+
+ my @packlists = $packer->packlists_containing(\@requires);
+ for my $packlist (@packlists) {
+ print "Packing $packlist\n";
+ }
+ $packer->packlists_to_tree($path, \@packlists);
+}
+
+sub make_fatlib {
+ my @modules = grep !in_lib(mod_to_pm($_)), find_requires($script_name);
+
+ pack_modules(cwd . '/fatlib', \@modules, []);
+
+ use Config;
+ print "Remove fatlib/$Config{archname}\n";
+ rmtree("fatlib/$Config{archname}");
+ rmtree("fatlib/POD2");
+
+ my $want = sub {
+ if (/\.pod$/) {
+ print "Remove $_\n";
+ unlink $_;
+ }
+ };
+
+ find({ wanted => $want, no_chdir => 1 }, 'fatlib');
+}
+
+
+sub generate_file {
+ my($base, $target, $fatpack) = @_;
+
+ open my $in, "<", $base;
+ open my $out, ">", "$target.tmp";
+
+ print STDERR "Generating $target from $base\n";
+
+ while (<$in>) {
+ s|^#!\h*perl|#!/usr/bin/env perl|;
+ s|^# FATPACK.*|$fatpack|;
+ print $out $_;
+ }
+
+ close $out;
+
+ eval { unlink $target };
+ rename "$target.tmp", $target;
+}
+
+sub make_script {
+ mkdir '.fatpack-build';
+ system qw(cp -r fatlib), $libdir, qw(.fatpack-build/);
+
+ my $fatpack_compact = do {
+ my $dir = pushd '.fatpack-build';
+
+ my @files;
+ my $want = sub {
+ push @files, $_ if /\.pm$/;
+ if (/\.pod$/) {
+ print "Remove $_\n";
+ unlink $_;
+ }
+ };
+
+ find({ wanted => $want, no_chdir => 1 }, 'fatlib', 'lib');
+ system qw(perlstrip --cache -v), @files;
+
+ `fatpack file`;
+ };
+
+ my $filename = $script_name;
+ $filename =~ s!^.*/!!;
+
+ generate_file($script_name, $filename, $fatpack_compact);
+ chmod 0755, $filename;
+}
+