root/lang/perl/Archer/trunk/lib/Archer.pm @ 29937

Revision 29937, 5.0 kB (checked in by walf443, 4 years ago)

ネストしたforループの罠にハマった

Line 
1package Archer;
2use strict;
3use warnings;
4use Carp;
5use List::MoreUtils qw/uniq/;
6use Archer::ConfigLoader;
7use UNIVERSAL::require;
8
9our $VERSION = '0.06';
10
11my $context;
12sub context { $context }
13
14sub set_context {
15    my ( $class, $c ) = @_;
16    $context = $c;
17}
18
19sub new {
20    my ( $class, $opts ) = @_;
21    my $self = bless { %$opts }, $class;
22
23    if ( !$$opts{ write_config } ) {
24        my $config_loader = Archer::ConfigLoader->new;
25        $self->{ config } = $config_loader->load( $opts->{ config_yaml }, $self );
26    }
27
28    if ( $self->{ log_level } ) {
29        $self->{ config }->{ global }->{ log } = { level => $self->{ log_level } };
30    } else {
31        $self->{ config }->{ global }->{ log } ||= { level => 'debug' };
32    }
33
34    Archer->set_context( $self );
35
36    return $self;
37}
38
39sub run {
40    my ( $self, ) = @_;
41
42    if ( $self->{ shell } ) {
43
44        # TODO: role support
45        require Archer::Shell;
46        my @servers
47            = map { @{ $_ } }
48            values
49            %{ $context->{ config }->{ projects }->{ $self->{ project } } };
50        my $shell = Archer::Shell->new(
51            {   context => $self,
52                config  => $self->{ config },
53                servers => \@servers,
54            }
55        );
56
57        $shell->run_loop;
58    }
59    elsif ( $self->{ write_config } ) {
60        require Archer::Util;
61        my $util = Archer::Util->new;
62        $util->templatize( $self );
63    }
64    else {
65        $self->run_hook( 'init' );
66
67        $self->run_process;
68
69        $self->run_hook( 'finalize' );
70    }
71}
72
73sub run_hook {
74    my ( $self, $hook, $args ) = @_;
75    $args ||= {};
76
77    $self->log( 'info' => "run hook $hook" );
78    TASK:
79    for my $plugin ( @{ $self->{ config }->{ tasks }->{ $hook } } ) {
80        if ( $self->{ skips }->{ $plugin->{ name } } ) {
81            $self->log( info => "skipped: $plugin->{name}" );
82            next;
83        }
84
85        if ( $hook eq 'process' && $self->{ only } ) {
86            if ( $self->{only} ne $plugin->{ name } ) {
87                $self->log( debug => "skipped: $plugin->{name}" );
88                next;
89            }
90        } else {
91            if ( $plugin->{skip_default} && ! $self->{ withs }->{ $plugin->{ name } } ) {
92                next;
93            }
94        }
95
96        for my $filter ( qw/ role project / ) {
97          if ( $plugin->{ $filter } && $plugin->{ $filter } ne $args->{ $filter } ) {
98              $self->log( info =>
99                      "skip $args->{server}. because $plugin->{$filter} ne $args->{$filter}"
100              );
101              next TASK;
102          }
103        }
104
105        my $class = ($plugin->{module} =~ /^\+(.+)$/) ? $1 : "Archer::Plugin::$plugin->{module}";
106        $self->log( 'debug' => "load $class" );
107        $class->use or die $@;
108
109        if ( $args->{server} ) {
110            $self->log( 'info' => "run @{[ $plugin->{name} ]} ( $class ) to @{[ $args->{server} ]}" );
111        } else {
112            $self->log( 'info' => "run @{[ $plugin->{name} ]} ( $class )" );
113        }
114        $class->new(
115            {   config  => $plugin->{ config },
116                project => $self->{ project },
117                %$args
118            }
119        )->run( $self, $args );
120
121        print "\n\n";    # for debug.
122    }
123}
124
125sub run_process {
126    my ( $self ) = @_;
127
128    my $parallel = $self->{ config }->{ global }->{ parallel }
129        || 'Archer::Parallel::ForkManager';
130    $parallel->use or die $@;
131
132    my $server_tree = $self->{config}->{projects}->{$self->{project}};
133
134    my @elems;
135    while ( my ( $role, $servers ) = each %$server_tree ) {
136        for my $server ( @$servers ) {
137            push @elems, { server => $server, role => $role };
138        }
139    }
140    $self->log( debug => "run parallel : $self->{parallel_num}" );
141    my $manager = $parallel->new;
142    $manager->run(
143        {   elems    => \@elems,
144            callback => sub {
145                my $args = shift;
146                $self->run_hook( 'process', $args );
147            },
148            num => $self->{ parallel_num },
149        }
150    );
151}
152
153sub bootstrap {
154    my ( $class, $opts ) = @_;
155
156    my $self = $class->new( $opts );
157    $self->run;
158    return $self;
159}
160
161# TODO: use the Log::Dispatch?
162sub log {
163    my ( $self, $level, $msg, %opt ) = @_;
164
165    return unless $self->should_log( $level );
166
167    # hack to get the original caller as Plugin or Rule
168    # from plagger.
169    my $caller = $opt{ caller };
170    unless ( $caller ) {
171        my $i = 0;
172        while ( my $c = caller( $i++ ) ) {
173            last if $c !~ /Plugin|Rule/;
174            $caller = $c;
175        }
176        $caller ||= caller( 0 );
177    }
178
179    warn "$caller [$level] $msg\n";
180}
181
182my %levels = (
183    debug => 0,
184    warn  => 1,
185    info  => 2,
186    error => 3,
187);
188
189sub should_log {
190    my ( $self, $level ) = @_;
191
192    my $setting_level = $self->{config}->{global}->{log}->{level} || 'debug';
193    $levels{ $level } >= $levels{ $setting_level };
194}
195
1961;
197
198__END__
199
200=head1 NAME
201
202Archer - yet another deployment tool
203
204=head1 DESCRIPTION
205
206This is yet another deployment tool :)
207
208=head1 AUTHORS
209
210Tokuhiro Matsuno and Archer committers.
211
212=head1 TODO
213
214=head1 SEE ALSO
215
216L<capistrano>
217
Note: See TracBrowser for help on using the browser.