File Coverage

blib/lib/GitHub/Actions.pm
Criterion Covered Total %
statement 60 63 95.2
branch 13 16 81.2
condition 4 4 100.0
subroutine 19 20 95.0
pod 12 12 100.0
total 108 115 93.9


line stmt bran cond sub pod time code
1             package GitHub::Actions;
2              
3 6     6   933483 use Exporter 'import'; # needed to use @EXPORT
  6         11  
  6         259  
4 6     6   27 use warnings;
  6         16  
  6         305  
5 6     6   25 use strict;
  6         9  
  6         162  
6 6     6   23 use Carp qw(croak);
  6         13  
  6         330  
7              
8 6     6   82 use v5.14;
  6         20  
9              
10             # Module implementation here
11             our %github;
12             our $EXIT_CODE = 0;
13              
14             our @EXPORT = qw(
15             %github $EXIT_CODE set_output set_env debug error warning
16             set_failed error_on_file warning_on_file
17             start_group end_group exit_action
18             );
19              
20             BEGIN {
21 6     6   64 for my $k ( keys(%ENV) ) {
22 165 100       239 if ( $k =~ /^GITHUB_/ ) {
23 4         11 my ($nogithub) = ( $k =~ /^GITHUB_(\w+)/ );
24 4         9 $github{$nogithub} = $ENV{$k} ;
25             }
26             }
27 6 50       138 if ($ENV{'GITHUB_REPOSITORY'}) {
28 0         0 my @repo_parts = split("/", $ENV{'GITHUB_REPOSITORY'});
29 0         0 $github{'REPO_NAME'} = pop @repo_parts;
30             }
31             }
32              
33             =head1 NAME
34              
35             GitHub::Actions - Work in GitHub Actions using native Perl
36              
37             =head1 VERSION
38              
39             This document describes GitHub::Actions version 0.2.2
40              
41             =cut
42              
43 6     6   2027 use version; our $VERSION = qv('0.2.2');
  6         8167  
  6         52  
44              
45             sub _write_to_github_file {
46 4     4   9 my ($github_var, $content) = @_;
47 4 50       792 open(my $fh, '>>', $github{$github_var}) or die "Could not open file ". $github{$github_var} ." $!";
48 4         101 say $fh $content;
49 4         204 close $fh;
50             }
51              
52             sub set_output {
53 2 50   2 1 184188 croak "Need name and value" unless @_;
54 2         7 my ($output_name, $output_value) = @_;
55 2   100     7 $output_value ||=1;
56 2         8 _write_to_github_file( 'OUTPUT', "$output_name=$output_value" );
57             }
58              
59             sub set_env {
60 2     2 1 718 my ($env_var_name, $env_var_value) = @_;
61 2   100     9 $env_var_value ||='1';
62 2         7 _write_to_github_file( 'ENV', "$env_var_name=$env_var_value" );
63             }
64              
65             sub debug {
66 1     1 1 266166 my $debug_message = shift;
67 1         140 say "::debug::$debug_message";
68             }
69              
70             sub error {
71 2     2 1 2515 my $error_message = shift;
72 2         6 $EXIT_CODE = 1;
73 2         139 say "::error::$error_message"
74             }
75              
76             sub warning {
77 2     2 1 2161 my $warning = shift;
78 2         108 say "::warning::$warning"
79             }
80              
81             sub error_on_file {
82 4     4 1 7480 $EXIT_CODE= 1;
83 4         14 command_on_file( "::error", @_ );
84             }
85              
86             sub warning_on_file {
87 2     2 1 4274 command_on_file( "::warning", @_ );
88             }
89              
90             sub command_on_file {
91 6     6 1 14 my $command = shift;
92 6         11 my $message = shift;
93 6 100       211 croak "Need at least a file name" unless @_;
94 5         13 my ($file, $line, $title, $col ) = @_;
95 5         8 my @data;
96 5         11 push( @data, "file=$file");
97 5 100       33 push( @data, "line=$line") if $line;
98 5 100       14 if ( $title ) {
99 3         19 push( @data, "title=$title"."::$message");
100             } else {
101 2         15 push( @data, "title=".uc(substr($command,2))."::$message");
102             }
103 5 100       16 push( @data, "col=$col") if $col;
104 5         16 $command .= " ".join(",", @data );
105 5         335 say $command;
106             }
107              
108             sub start_group {
109 1     1 1 2202 say "::group::" . shift;
110             }
111              
112             sub end_group {
113 1     1 1 32 say "::endgroup::";
114             }
115              
116             sub set_failed {
117 1     1 1 219681 error( @_ );
118 1         7 exit( 1);
119             }
120              
121             sub exit_action {
122 0     0 1   exit( $EXIT_CODE );
123             }
124              
125             "Action!"; # Magic true value required at end of module
126             __END__
127              
128              
129             =head1 SYNOPSIS
130              
131             This will be in the context of a GitHub action step. You will need to install
132             via CPAN this module first, and use C<perl {0}> as C<shell>. Please see below
133             this code for instructions.
134              
135             use GitHub::Actions;
136             use v5.14;
137              
138             # Imported %github contains all GITHUB_* environment variables
139             for my $g (keys %github ) {
140             say "GITHUB_$g -> ", $github{$g}
141             }
142              
143             # The name of the repository itself, without the name part, is stored in a new variable
144             say $github{'REPO_NAME'};
145              
146             # These are commands that mirror those in GitHub actions
147              
148             # Set step output
149             set_output("FOO", "BAR");
150              
151             # Set environment variable value
152             set_env("FOO", "BAR");
153              
154             # Produces an error and sets exit code to 1
155             error( "FOO has happened" );
156              
157             # Error/warning with information on file. The last 3 parameters are optional
158             error_on_file( "There's foo", $file, $line, "Error", $col ); # and sets exit code to 1
159             warning_on_file( "There's bar", $file, $line, "Warning", $col );
160              
161             # Debugging messages and warnings
162             debug( "Value of FOO is $bar" );
163             warning( "Value of FOO is $bar" );
164              
165             # Start and end group
166             start_group( "Foo" );
167             # do stuff
168             end_group;
169              
170             # Exits with error if that's the case
171             exit_action();
172              
173             # Errors and exits
174             set_failed( "We're doomed" );
175              
176             Install this module within a GitHub action, as a C<step>
177              
178             - name: "Install GitHub::Actions"
179             run: sudo cpan GitHub::Actions
180              
181             (we need C<sudo> since we're using the system Perl that's installed in every runner)
182              
183             Then, as another C<step>
184              
185             - name: Test env variables
186             shell: perl {0}
187             run: |
188             use GitHub::Actions;
189             set_env( 'FOO', 'BAR');
190              
191             In most cases, you'll want to just have it installed locally in your repository
192             and C<fatpack> it in a script that you will upload it to the repository.
193              
194             =head1 DESCRIPTION
195              
196             GitHub Actions include by default, at least in its Linux runners, a
197             system Perl which you can use directly in them. This here is
198             a (for the time being) minimalistic module that tries to help a bit
199             with that, by defining a few functions that will be useful when
200             performing GitHub actions. Besides the system Perl, you can use any of
201             L<the modules
202             installed|https://gist.github.com/JJ/edf3a39d68525439978da2a02763d42b>. You
203             can install other modules via cpan or, preferably for speed, via the
204             Ubuntu package (or equivalent).
205              
206             Check out an example of using it in the
207             L<repository|https://github.com/JJ/perl-GitHub-Actions/blob/main/.github/workflows/self-test.yml>.
208              
209             =head1 INTERFACE
210              
211             =head2 set_env( $env_var_name, $env_var_value)
212              
213             This is equivalent to
214             L<setting an environment variable|https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable>
215              
216             =head2 set_output( $output_name, $output_value)
217              
218             Equivalent to L<C<set_output>|https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#setting-an-output-parameter>
219              
220             =head2 set_failed( $error_message )
221              
222             Sets the step as failed with the indicated error message and exits.
223              
224             =head2 debug( $debug_message )
225              
226             Equivalent to L<C<debug>|https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#setting-a-debug-message>
227              
228             =head2 error( $error_message )
229              
230             Equivalent to
231             L<C<error>|https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#setting-an-error-message>,
232             prints an error message. Remember to call L<exit_action()> to make the step fail
233             if there's been some error.
234              
235             =head2 warning( $warning_message )
236              
237             Equivalent to L<C<warning>|https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-a-warning-message>, simply prints a warning.
238              
239             =head2 command_on_file( $error_message, $file, $line, $title, $col )
240              
241             Common code for L<error_on_file> and L<warning_on_file>. Can be used for any future commands.
242              
243             =head2 error_on_file( $error_message, $file, $line, $title, $col )
244              
245             Equivalent to
246             L<C<error>|https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#setting-an-error-message>,
247             prints an error message with file and line info. Also set the exit status to 1,
248             so once again remember to call C<exit_action> after calling this.
249              
250             =head2 warning_on_file( $warning_message, $file, $line, $title, $col )
251              
252             Equivalent to L<C<warning>|https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-a-warning-message>, prints an warning with file and line info.
253              
254             =head2 set_failed( $error_message )
255              
256             Exits with an error status of 1 after setting the error message.
257              
258             =head2 start_group( $group_name )
259              
260             Starts a group in the logs, grouping the following messages. Corresponds to
261             L<C<group>|https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#grouping-log-lines>.
262              
263             =head2 end_group
264              
265             Ends current log grouping.
266              
267             =head2 exit_action
268              
269             Exits with the exit code generated during run, that is, 1 if there's been any
270             error reported.
271              
272             =head1 CONFIGURATION AND ENVIRONMENT
273              
274             GitHub::Actions requires no configuration files or environment
275             variables. Those set by GitHub Actions will only be available there,
276             or if you set them explicitly. Remember that they will need to be set
277             during the C<BEGIN> phase to be available when this module loads.
278              
279             BEGIN {
280             $ENV{'GITHUB_FOO'} = 'foo';
281             $ENV{'GITHUB_BAR'} = 'bar';
282             }
283              
284             You can use this for testing, for instance, if you create any module based on
285             this one.
286              
287             =head1 DEPENDENCIES
288              
289             Intentionally, no dependencies are included. Several dependencies are used for
290             testing, though.
291              
292             =head1 INCOMPATIBILITIES
293              
294             None reported.
295              
296             =head1 BUGS AND LIMITATIONS
297              
298             No bugs have been reported.
299              
300             Please report any bugs or feature requests to L<https://github.com/JJ/perl-GitHub-Actions/issues>.
301              
302             =head1 AUTHOR
303              
304             JJ Merelo C<< <jmerelo@CPAN.org> >>. Many thanks to RENEEB and Gabor Szabo for their help with test and metadata.
305              
306              
307             =head1 LICENCE AND COPYRIGHT
308              
309             Copyright (c) 2021, 2022 JJ Merelo C<< <jmerelo@CPAN.org> >>. All rights
310             reserved.
311              
312             This module is free software; you can redistribute it and/or
313             modify it under the same terms as Perl itself. See L<perlartistic>.
314              
315              
316             =head1 DISCLAIMER OF WARRANTY
317              
318             BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
319             FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
320             OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
321             PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
322             EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
323             WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
324             ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
325             YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
326             NECESSARY SERVICING, REPAIR, OR CORRECTION.
327              
328             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
329             WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
330             REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
331             LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
332             OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
333             THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
334             RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
335             FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
336             SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
337             SUCH DAMAGES.