File Coverage

blib/lib/Cinnamon.pm
Criterion Covered Total %
statement 37 50 74.0
branch 4 10 40.0
condition 1 6 16.6
subroutine 10 10 100.0
pod 1 2 50.0
total 53 78 67.9


line stmt bran cond sub pod time code
1             package Cinnamon;
2 3     3   69574 use strict;
  3         5  
  3         87  
3 3     3   14 use warnings;
  3         6  
  3         85  
4 3     3   57 use 5.010_001;
  3         14  
5              
6             our $VERSION = '0.13_02';
7              
8 3     3   1317 use YAML ();
  3         18529  
  3         92  
9 3     3   1412 use Class::Load ();
  3         41616  
  3         98  
10              
11 3     3   1280 use Cinnamon::Config;
  3         14  
  3         86  
12 3     3   1519 use Cinnamon::Runner;
  3         10  
  3         89  
13 3     3   17 use Cinnamon::Logger;
  3         25  
  3         880  
14              
15             sub new {
16 9     9 0 18 my $class = shift;
17 9         72 bless { }, $class;
18             }
19              
20             sub run {
21 9     9 1 50 my ($self, $role, $task, %opts) = @_;
22 9         51 my @args = Cinnamon::Config::load $role, $task, %opts;
23              
24 9 100       37 if ($opts{info}) {
25 1         8 log 'info', YAML::Dump(Cinnamon::Config::info);
26 1         25 return;
27             }
28              
29 8         30 my $hosts = Cinnamon::Config::get_role;
30 8         31 my $task_def = Cinnamon::Config::get_task;
31 8   50     26 my $runner = Cinnamon::Config::get('runner_class') || 'Cinnamon::Runner::Concurrent';
32              
33 8 50       24 unless (defined $hosts) {
34 0         0 log 'error', "undefined role : '$role'";
35 0         0 return;
36             }
37 8 50       30 unless (defined $task_def) {
38 0         0 log 'error', "undefined task : '$task'";
39 0         0 return;
40             }
41              
42 8         52 Class::Load::load_class $runner;
43              
44 6         692 my $result = $runner->start($hosts, $task_def);
45 0           my (@success, @error);
46              
47 0 0         for my $key (keys %{$result || {}}) {
  0            
48 0 0         if ($result->{$key}->{error}) {
49 0           push @error, $key;
50             }
51             else {
52 0           push @success, $key;
53             }
54             }
55              
56 0   0       log success => sprintf(
57             "\n========================\n[success]: %s",
58             (join(', ', @success) || ''),
59             );
60              
61 0   0       log error => sprintf(
62             "[error]: %s",
63             (join(', ', @error) || ''),
64             );
65              
66 0           return (\@success, \@error);
67             }
68              
69             !!1;
70              
71             __END__
72              
73             =encoding utf8
74              
75             =head1 NAME
76              
77             Cinnamon - A minimalistic deploy tool
78              
79             =head1 SYNOPSIS
80              
81             use strict;
82             use warnings;
83              
84             # Exports some commands
85             use Cinnamon::DSL;
86              
87             my $application = 'My::App';
88              
89             # It's required if you want to login to remote host
90             set user => 'johndoe';
91              
92             # User defined params to use later
93             set application => $application;
94             set repository => "git://git.example.com/projects/$application";
95              
96             # Lazily evaluated if passed as a code
97             set lazy_value => sub {
98             #...
99             };
100              
101             # Roles
102             role development => 'development.example.com', {
103             deploy_to => "/home/app/www/$application-devel",
104             branch => "develop",
105             };
106              
107             # Lazily evaluated if passed as a code
108             role production => sub {
109             my $res = LWP::UserAgent->get('http://servers.example.com/api/hosts');
110             my $hosts = decode_json $res->content;
111             $hosts;
112             }, {
113             deploy_to => "/home/app/www/$application",
114             branch => "master",
115             };
116              
117             # Tasks
118             task update => sub {
119             my ($host, @args) = @_;
120             my $deploy_to = get('deploy_to');
121             my $branch = 'origin/' . get('branch');
122              
123             # Executed on localhost
124             run 'some', 'command';
125              
126             # Executed on remote host
127             remote {
128             run "cd $deploy_to && git fetch origin && git checkout -q $branch && git submodule update --init";
129             } $host;
130             };
131             task restart => sub {
132             my ($host, @args) = @_;
133             # ...
134             };
135              
136             # Nest tasks
137             task server => {
138             setup => sub {
139             my ($host, @args) = @_;
140             # ...
141             },
142             };
143              
144             =head1 WARNINGS
145              
146             This software is under the heavy development and considered ALPHA quality. Things might be broken, not all features have been implemented, and APIs will be likely to change.
147              
148             =head1 DESCRIPTION
149              
150             Cinnamon is a minimalistic deploy tool aiming to provide
151             structurization of issues about deployment. It only introduces the
152             most essential feature for deployment and a few utilities.
153              
154             =head1 DSLs
155              
156             This module provides some DSLs for use. I designed them to be kept as
157             simple as possible, and I don't want to add too many commands:
158              
159             =head2 Structural Commands
160              
161             =head3 role ( I<$role: Str> => (I<$host: String> | I<$hosts: Array[String]> | I<$sub: CODE>), I<$param: HASHREF> )
162              
163             =over 4
164              
165             role production => 'production.example.com';
166              
167             # or
168              
169             role production => [ qw(production1.example.com production2.exampl.ecom) ];
170              
171             # or
172              
173             role production => sub {
174             my $res = LWP::UserAgent->get('http://servers.example.com/api/hosts');
175             my $hosts = decode_json $res->content;
176             $hosts;
177             };
178              
179             # or
180              
181             role production => 'production.example.com', {
182             hoge => 'fuga',
183             };
184              
185             Relates names (eg. production) to hosts to be deployed.
186              
187             If you pass a CODE as the second argument, this method delays the
188             value to be evaluated till the value is needed at the first time. This
189             is useful, for instance, when you want to retrieve hosts information
190             from some external APIs or so.
191              
192             If you pass a HASHREF as the third argument, you can get specified
193             parameters by get DSL.
194              
195             =back
196              
197             =head3 task ( I<$taskname: Str> => (I<\%tasks: Hash[String => CODE]> | I<$sub: CODE>) )
198              
199             =over 4
200              
201             task update => sub {
202             my ($host, @args) = @_;
203             my $hoge = get 'hoge'; # parameter set in global or role parameter
204             # ...
205             };
206              
207             # you can nest tasks
208             task server => {
209             start => sub {
210             my ($host, @args) = @_;
211             # ...
212             },
213             stop => sub {
214             my ($host, @args) = @_;
215             # ...
216             },
217             };
218              
219             Defines some named tasks by CODEs.
220              
221             The arguments which are passed into the CODEs are:
222              
223             =over 4
224              
225             =item * I<$host>
226              
227             The host name where the task is executed. Which is one of the hosts
228             you set by C<role> command.
229              
230             =item * I<@args>
231              
232             Command line argument which is passed by user.
233              
234             $ cinammon production update foo bar baz
235              
236             In case above, C<@args> contains C<('foo', 'bar', 'baz')>.
237              
238             =back
239              
240             =back
241              
242             =head2 Utilities
243              
244             =head3 set ( I<$key: String> => (I<$value: Any> | I<$sub: CODE>) )
245              
246             =over 4
247              
248             set key => 'value';
249              
250             # or
251              
252             set key => sub {
253             # values to be lazily evaluated
254             };
255              
256             # or
257              
258             set key => sub {
259             my (@args) = @_;
260             # value to be lazily evaluated with @args
261             };
262              
263             Sets a value which is related to a key.
264              
265             If you pass a CODE as the second argument, this method delays the
266             value to be evaluated till C<get> is called. This is useful when you
267             want to retrieve hosts information from some external APIs or so.
268              
269             =back
270              
271             =head3 get ( I<$key: String> [, I<@args: Array[Any]> ] ): Any
272              
273             =over 4
274              
275             my $value = get 'key';
276              
277             # or
278              
279             my $value = get key => qw(foo bar baz);
280              
281             Gets a value related to the key.
282              
283             If the value is a CODE, you can pass some arguments which can be used
284             while evaluating.
285              
286             =back
287              
288             =head3 run ( I<@command: Array> ): ( I<$stdout: String>, I<$stderr: String> )
289              
290             =over 4
291              
292             my ($stdout, $stdout) = run 'git', 'pull';
293              
294             Executes a command. It returns the result of execution, C<$stdout> and
295             C<$stderr>, as strings.
296              
297             =back
298              
299             =head3 sudo ( I<@command: Array> ): ( I<$stdout: String>, I<$stderr: String> )
300              
301             =over 4
302              
303             my ($stdout, $stdout) = sudo '/path/to/httpd', 'restart';
304              
305             Executes a command as well, but under I<sudo> environment.
306              
307             =back
308              
309             =head3 remote ( I<$sub: CODE> I<$host: String> ): Any
310              
311             =over 4
312              
313             my ($stdout, $stdout) = remote {
314             run 'git', 'pull';
315             sudo '/path/to/httpd', 'restart';
316             } $host;
317              
318             Connects to the remote C<$host> and executes the C<$code> there.
319              
320             Where C<run> and C<sudo> commands to be executed depends on that
321             context. They are done on the remote host when set in C<remote> block,
322             whereas done on localhost without it.
323              
324             Remote login username is retrieved by C<get 'user'> or C<`whoami`>
325             command. Set appropriate username in advance if needed.
326              
327             =back
328              
329             =head1 REPOSITORY
330              
331             https://github.com/kentaro/cinnamon
332              
333             =head1 AUTHOR
334              
335             =over 4
336              
337             =item * Kentaro Kuribayashi E<lt>kentarok@gmail.comE<gt>
338              
339             =item * Yuki Shibazaki E<lt>shibayu36 at gmail.comE<gt>
340              
341             =back
342              
343             =head1 SEE ALSO
344              
345             =over 4
346              
347             =item * Tutorial (Japanese)
348              
349             L<http://d.hatena.ne.jp/naoya/20130118/1358477523>
350              
351             =item * L<capistrano>
352              
353             =item * L<Archer>
354              
355             =back
356              
357             =head1 LICENSE
358              
359             Copyright (C) Kentaro Kuribayashi
360              
361             This library is free software; you can redistribute it and/or modify
362             it under the same terms as Perl itself.
363              
364             =cut