File Coverage

blib/lib/Config/Identity.pm
Criterion Covered Total %
statement 96 99 96.9
branch 34 48 70.8
condition 8 13 61.5
subroutine 16 16 100.0
pod 2 10 20.0
total 156 186 83.8


line stmt bran cond sub pod time code
1 1     1   25594 use strict;
  1         11  
  1         26  
2 1     1   3 use warnings;
  1         1  
  1         76  
3              
4             package Config::Identity;
5             # ABSTRACT: Load (and optionally decrypt via GnuPG) user/pass identity information
6              
7             our $VERSION = '0.0019';
8              
9 1     1   747 use Carp;
  1         1  
  1         60  
10 1     1   934 use IPC::Run qw/ start finish /;
  1         37066  
  1         54  
11 1     1   492 use File::HomeDir();
  1         4636  
  1         28  
12 1     1   6 use File::Spec;
  1         1  
  1         9  
13              
14             our $home = File::HomeDir->home;
15             {
16             my $gpg;
17 4 50 66 4 0 514 sub GPG() { $ENV{CI_GPG} || ( $gpg ||= do {
18 1         8 require File::Which;
19 1   50     8 $gpg = File::Which::which( $_ ) and last for qw/ gpg gpg2 /;
20 1         197 $gpg;
21             } ) }
22             }
23 3 50   3 0 13 sub GPG_ARGUMENTS() { $ENV{CI_GPG_ARGUMENTS} || '' }
24              
25             # TODO Do not even need to do this, since the file is on disk already...
26             sub decrypt {
27 3     3 0 8 my $self = shift;
28 3         6 my $file = shift;
29              
30 3 50       19 my $gpg = GPG or croak "Missing gpg";
31 3         11 my $gpg_arguments = GPG_ARGUMENTS;
32 3         4 my $run;
33             # Old versions, please ignore
34             #$run = "$gpg $gpg_arguments -qd --no-tty --command-fd 0 --status-fd 1";
35             #$run = "$gpg $gpg_arguments -qd --no-tty --command-fd 0";
36 3         12 $run = "$gpg $gpg_arguments -qd --no-tty";
37 3         37 my @run = split m/\s+/, $run;
38 3         9 push @run, $file;
39 3         40 my $process = start( \@run, '>pipe', \*OUT, '2>pipe', \*ERR );
40 3         79371 my $output = join '', ;
41 3         68 my $_error = join '', ;
42 3         35 finish $process;
43 3         1355 return ( $output, $_error );
44             }
45              
46             sub best {
47 12     12 0 97 my $self = shift;
48 12         16 my $stub = shift;
49 12         14 my $base = shift;
50 12 100       33 $base = $home unless defined $base;
51              
52 12 50 33     76 croak "Missing stub" unless defined $stub && length $stub;
53              
54 12         60 for my $i0 ( ".$stub-identity", ".$stub" ) {
55 21         25 for my $i1 ( "." ) {
56 21         145 my $path = File::Spec->catfile( $base, $i1, $i0 );
57 21 100       384 return $path if -f $path;
58             }
59             }
60              
61 0         0 return '';
62             }
63              
64             sub read {
65 11     11 0 4404 my $self = shift;
66 11         10 my $file = shift;
67              
68 11 50       102 croak "Missing file" unless -f $file;
69 11 50       130 croak "Cannot read file ($file)" unless -r $file;
70              
71 11         435 my $binary = -B $file;
72              
73 11 50       325 open my $handle, $file or croak $!;
74 11 100       31 binmode $handle if $binary;
75 11         48 local $/ = undef;
76 11         113 my $content = <$handle>;
77 11 50       64 close $handle or warn $!;
78              
79 11 100 100     78 if ( $binary || $content =~ m/----BEGIN PGP MESSAGE----/ ) {
80 3         30 my ( $_content, $error ) = $self->decrypt( $file );
81 3 50       187 if ( $error ) {
82 0 0       0 carp "Error during decryption of content" . $binary ? '' : "\n$content";
83 0         0 croak "Error during decryption of $file:\n$error";
84             }
85 3         8 $content = $_content;
86             }
87            
88 11         140 return $content;
89             }
90              
91             sub parse {
92 11     11 0 53 my $self = shift;
93 11         19 my $content = shift;
94              
95 11 50       25 return unless $content;
96 11         13 my %content;
97 11         40 for ( split m/\n/, $content ) {
98 32 100       76 next if /^\s*#/;
99 31 100       81 next unless m/\S/;
100 29 100       124 next unless my ($key, $value) = /^\s*(\w+)\s+(.+)$/;
101 22         65 $content{$key} = $value;
102             }
103 11         84 return %content;
104             }
105              
106             sub load_best {
107 6     6 1 6 my $self = shift;
108 6         6 my $stub = shift;
109              
110 6 50       16 croak "Unable to find .$stub-identity or .$stub" unless my $path = $self->best( $stub );
111 6         14 return $self->load( $path );
112             }
113              
114             sub try_best {
115 4     4 0 8 my $self = shift;
116 4         6 my $stub = shift;
117              
118 4 50       14 return unless my $path = $self->best( $stub );
119 4         21 return $self->load( $path );
120             }
121              
122             sub load {
123 10     10 0 13 my $self = shift;
124 10         13 my $file = shift;
125              
126 10         24 return $self->parse( $self->read( $file ) );
127             }
128              
129             sub load_check {
130 6     6 1 4718 my $self = shift;
131 6         7 my $stub = shift;
132 6   50     15 my $required = shift || [];
133              
134 6         12 my %identity = $self->load_best($stub);
135 6         7 my @missing;
136 6 100       20 if ( ref $required eq 'ARRAY' ) {
    100          
137 3         6 @missing = grep { ! defined $identity{$_} } @$required;
  7         16  
138             }
139             elsif ( ref $required eq 'CODE' ) {
140 2         3 local $_ = \%identity;
141 2         5 @missing = $required->(\%identity);
142             }
143             else {
144 1         165 croak "Argument to check keys must be an arrayref or coderef";
145             }
146              
147 5 100       1506 if ( @missing ) {
148 3 100       8 my $inflect = @missing > 1 ? "fields" : "field";
149 3         318 croak "Missing required ${inflect}: @missing"
150             }
151              
152 2         9 return %identity;
153             }
154              
155             1;
156              
157             __END__