]> git.donarmstrong.com Git - bin.git/blob - stodo
* Add stodo command which will automatically commit changes to todo files backed...
[bin.git] / stodo
1 #! /usr/bin/perl
2 # ss makes a screenshot of the screen using import, and is released
3 # under the terms of the GPL version 2, or any later version, at your
4 # option. See the file README and COPYING for more information.
5 # Copyright 2004 by Don Armstrong <don@donarmstrong.com>.
6 # $Id: ss,v 1.3 2004/09/27 05:58:55 don Exp $
7
8
9 use warnings;
10 use strict;
11
12
13 use Getopt::Long;
14 use Pod::Usage;
15
16 =head1 NAME
17
18 stodo - devtodo event script which attempts to commit the changes made to a .todo file after a change
19
20 =head1 SYNOPSIS
21
22 stodo [options]
23
24  Options:
25   --simulate, -s simulate: don't actually do anything
26   --quiet, -q don't output to STDOUT
27   --debug, -d debugging level (Default 0)
28   --help, -h display this help
29   --man, -m display manual
30
31 =head1 OPTIONS
32
33 =over
34
35 =item B<--simulate, -s>
36
37 Simulation mode, don't actually commit anything.
38
39 =item B<--quiet, -q>
40
41 Don't output anything to STDOUT
42
43 =item B<--debug, -d>
44
45 Debug verbosity. (Default 0)
46
47 =item B<--help, -h>
48
49 Display brief useage information.
50
51 =item B<--man, -m>
52
53 Display this manual.
54
55 =back
56
57 =head1 EXAMPLES
58
59  on save {
60     exec stodo
61  }
62
63
64 =cut
65
66
67
68 use Cwd qw(cwd);
69 use IO::File;
70 use XML::Simple;
71 use IO::Handle;
72 use Data::Diff qw(Diff);
73 use File::Basename qw(dirname basename);
74
75 use vars qw($DEBUG);
76
77 # XXX parse config file
78
79 my %options = (quiet           => 0,
80                debug           => 0,
81                help            => 0,
82                man             => 0,
83                simulate        => 0,
84               );
85
86 GetOptions(\%options,'debug|d+','help|h|?','man|m','quiet|q+','simulate|s+');
87
88 pod2usage() if $options{help} or not exists $ENV{TODODB};
89 pod2usage({verbose=>2}) if $options{man};
90
91 $DEBUG = $options{debug};
92
93 # Figure out what we're doing
94 my $tododb = $ENV{TODODB};
95 if ($tododb !~ m#^/#) {
96      $tododb = cwd().'/'.$tododb;
97 }
98 $tododb = full_readlink($tododb);
99 my $base_dir = dirname($tododb);
100 my $tododb_name = basename($tododb);
101
102 if (not -e "$base_dir/.svn") {
103      print "$base_dir/.svn doesns't exist, not commiting" unless $options{quiet};
104      exit 0;
105 }
106
107 # Make sure that we can ping the subversion server
108 my $svn_entries_fh = new IO::File "$base_dir/.svn/entries",'r'
109      or die "unable to open $base_dir/.svn/entries: $!";
110 my $url_type;
111 my $svn_host;
112 while (<$svn_entries_fh>) {
113      next unless m#^\s+url=\"(http|svn\+ssh|file):///?([^\/]+)#;
114      $url_type = $1;
115      last if $url_type eq 'file';
116      $svn_host = $2;
117      last;
118 }
119
120 if (not defined $svn_host and $url_type ne 'file') {
121      die "Was unable to find which host the svn repository is located on";
122 }
123 if ($url_type ne 'file') {
124      qx(ping -q -c 3 $svn_host >/dev/null 2>&1);
125      if ($? != 0) {
126           print "Unable to ping $svn_host" unless $options{quiet};
127           exit 0 unless $options{simulate};
128      }
129 }
130
131
132 # See what has changed
133 #my $svn_fh = new IO::Handle;
134 #open($svn_fh,'-|','svn','diff',$tododb) or die "Unable to execute svn diff $tododb; $!";
135 #while (<$svn_fh>) {
136 #     next unless /^[-+]/;
137 #}
138
139 my $todo_db_old = fix_content(XMLin("$base_dir/.svn/text-base/${tododb_name}.svn-base",KeyAttr=>'time')->{note});
140 my $todo_db_new = fix_content(XMLin($tododb,KeyAttr=>'time')->{note});
141
142 my @commit_message;
143
144 my $diff = Diff($todo_db_new,$todo_db_old);
145
146 #use Data::Dumper;
147 #print Dumper($diff);
148
149 if (exists $diff->{uniq_a}) {
150      foreach (values %{$diff->{uniq_a}}) {
151           my ($content,$priority) = map {s/^\s*|\s*$//g; s/\n/ /g; $_;} @{$_}{qw(content priority)};
152           push @commit_message,qq( * Added todo: [$priority] $content);
153      }
154 }
155
156 if (exists $diff->{uniq_b}) {
157      foreach (values %{$diff->{uniq_b}}) {
158           my ($content,$priority) = map {s/^\s*|\s*$//g; s/\n/ /g; $_;} @{$_}{qw(content priority)};
159           push @commit_message,qq( * Deleted todo: [$priority] $content);
160      }
161 }
162
163 if (exists $diff->{diff}) {
164      # This entry has either been edited, completed, or uncompleted
165      foreach my $entry_key (keys %{$diff->{diff}}) {
166           my ($content,$priority) = map {s/^\s*|\s*$//g; s/\n/ /g; $_;} @{$todo_db_new->{$entry_key}}{qw(content priority)};
167           my $diff_entry = $diff->{diff}{$entry_key};
168           if (exists $diff_entry->{uniq_a} and exists $diff_entry->{uniq_a}{done}) {
169                push @commit_message,qq( * Finished todo: [$priority] $content);
170           }
171           if (exists $diff_entry->{uniq_a} and exists $diff_entry->{uniq_b}{done}) {
172                push @commit_message,qq( * Marked as undone: [$priority] $content);
173           }
174           if (exists $diff_entry->{diff}) {
175                my @what_changed;
176                if (exists $diff_entry->{diff}{content}) {
177                     push @what_changed,'content';
178                }
179                if (exists $diff_entry->{diff}{priority}) {
180                     push @what_changed,'priority';
181                }
182                if (not @what_changed) {
183                     @what_changed = keys %{$diff_entry->{diff}};
184                }
185                push @commit_message,qq( * Todo changed ).join(' and ',@what_changed).qq( [$priority] $content);
186           }
187      }
188 }
189
190 if (exists $diff->{same}) {
191      foreach my $entry_key (keys %{$diff->{same}}) {
192           my ($content,$priority) = map {s/^\s*|\s*$//g; s/\n/ /g; $_;} @{$todo_db_new->{$entry_key}}{qw(content priority)};
193           my $diff_entry = $diff->{same}{$entry_key};
194           if (exists $diff_entry->{uniq_a} and exists $diff_entry->{uniq_a}{done}) {
195                push @commit_message,qq( * Finished todo: [$priority] $content);
196           }
197           if (exists $diff_entry->{uniq_a} and exists $diff_entry->{uniq_b}{done}) {
198                push @commit_message,qq( * Marked as undone: [$priority] $content);
199           }
200           if (exists $diff_entry->{diff}) {
201                my @what_changed;
202                if (exists $diff_entry->{diff}{content}) {
203                     push @what_changed,'content';
204                }
205                if (exists $diff_entry->{diff}{priority}) {
206                     push @what_changed,'priority';
207                }
208                if (not @what_changed) {
209                     @what_changed = keys %{$diff_entry->{diff}};
210                }
211                push @commit_message,qq( * Todo changed ).join(' and ',@what_changed).qq( [$priority] $content);
212           }
213      }
214 }
215
216
217 if (not @commit_message) {
218      print "No commit message\n" if $DEBUG or $options{simulate};
219      exit 0;
220 }
221
222 my $commit_message = join('', map {qq($_\n)} @commit_message);
223 if ($options{simulate}) {
224      print "svn commit -F - $tododb <<EOF\n";
225      print $commit_message;
226      print "EOF\n";
227 }
228 else {
229      system('svn','commit','-m',$commit_message,$tododb);
230 }
231
232
233 # Commit the changes with an appropriate commit message
234
235
236 sub fix_content {
237      my ($s) = @_;
238      foreach my $entry (values %{$s}) {
239           if (exists $entry->{content}) {
240                $entry->{content} =~ s/^\s*|\s*$//g;
241           }
242           if (exists $entry->{comment}) {
243                $entry->{comment} =~ s/^\s*|\s*$//g;
244           }
245      }
246      return $s;
247 }
248
249
250 sub full_readlink {
251      my ($file) = @_;
252      while (-l $file) {
253           $file = readlink $file;
254      }
255
256      return $file;
257 }
258
259 __END__