NAME
App::GHGen::PerlCustomizer - Customize Perl workflows based on project requirements
SYNOPSIS
use App::GHGen::PerlCustomizer qw(detect_perl_requirements);
my $requirements = detect_perl_requirements();
# Returns: { min_version => '5.036', has_cpanfile => 1, ... }
FUNCTIONS
detect_perl_requirements()
Detect Perl version requirements and dependency-file presence in the current directory.
Purpose
Inspect the current working directory for common Perl distribution files (cpanfile, Makefile.PL, dist.ini, Build.PL) and extract the minimum Perl version declared in any of them.
Arguments
None.
Returns
A hash reference with the following keys, all of which are always present:
{
min_version => Str | undef, # e.g. '5.036'; undef when undetected
has_cpanfile => Bool,
has_makefile_pl => Bool,
has_dist_ini => Bool,
has_build_pl => Bool,
}
Side Effects
Reads files from the current working directory.
Usage Example
use App::GHGen::PerlCustomizer qw(detect_perl_requirements);
my $reqs = detect_perl_requirements();
say $reqs->{min_version} // 'not specified';
API SPECIFICATION
Input
# No parameters.
Output
{
type => 'hashref',
keys => {
min_version => { type => 'scalar', optional => 1 },
has_cpanfile => { type => 'scalar' },
has_makefile_pl => { type => 'scalar' },
has_dist_ini => { type => 'scalar' },
has_build_pl => { type => 'scalar' },
},
}
FORMAL SPECIFICATION
detect_perl_requirements : → Requirements
Requirements ≔ {
min_version: ℤ* ∪ { ⊥ },
has_cpanfile: 𝔹,
has_makefile_pl: 𝔹,
has_dist_ini: 𝔹,
has_build_pl: 𝔹,
}
min_version ≔
cpanfile exists ∧ version parseable from cpanfile → version
Makefile.PL exists ∧ MIN_PERL_VERSION parseable → version
otherwise → ⊥
generate_custom_perl_workflow($options)
Generate a customized Perl CI workflow and return it as a YAML string.
Options:
perl_versions(array ref)-
Explicit list of Perl versions to include in the test matrix, e.g.
['5.40', '5.38', '5.36']. Takes precedence overmin_perl_versionandmax_perl_versionwhen supplied. min_perl_version(string, default'5.36')-
Lowest Perl version to include in the matrix when
perl_versionsis not given. Accepts both'5.036'and'5.36'notation. max_perl_version(string, default'5.42')-
Highest Perl version to include in the default matrix when
perl_versionsis not given. The built-in version table currently spans5.22through5.44; the default produces the matrix5.36, 5.38, 5.40, 5.42.To test against Perl 5.44 (opt-in, not tested by default), pass
max_perl_version => '5.44'or setperl_versionsexplicitly:generate_custom_perl_workflow({ max_perl_version => '5.44' }); # matrix: 5.36, 5.38, 5.40, 5.42, 5.44 generate_custom_perl_workflow({ perl_versions => ['5.42', '5.44'] }); # matrix: 5.42, 5.44 os(array ref)-
Operating systems for the build matrix. Default:
['ubuntu-latest', 'macos-latest', 'windows-latest']. timeout(integer, default30)-
Value written to
timeout-minuteson every matrix job. enable_linter(boolean, default1)-
When true, inserts a "Lint and syntax check" step after all dependency installation steps and before the test run. The step runs on every matrix cell (all OS and Perl version combinations) so that compile-time errors are caught across the full version range being tested.
The step uses
shell: perl {0}, which works identically on Linux, macOS, and Windows without any OS-specific branching. It searcheslib/andbin/(falling back to.when neither exists), loads each.pmfile viado $filewith a stub@INChandler that silently satisfies any missingusestatements, and exits non-zero if any file fails. Usingdo $fileavoids spawningperl -csubprocesses (which have Windows command-line quoting issues) and avoids false failures caused by missing CPAN dependencies. No additional CPAN modules are required. enable_linter_unused(boolean, default1)-
When true, appends an unused-variable check to the end of the lint step (i.e. it runs before the test run, not after). The check installs warnings::unused from CPAN, then runs
PERL5OPT=-Mwarnings::unused prove -lr t/so that variable lifetimes are exercised at runtime (perl -cis compile-only and cannot detect unused variables). It is gated onRUNNER_OS == Linuxand markedcontinue-on-error: truebecause unused-variable warnings can be legitimately noisy on some codebases. enable_critic(boolean, default1)-
When true, adds a Perl::Critic step on the latest matrix Perl version and
ubuntu-latest. enable_coverage(boolean, default1)-
When true, adds a Devel::Cover test-coverage step on the latest matrix Perl version and
ubuntu-latest. enable_perlimports(boolean, default1)-
When true, adds a "Check imports with perlimports" step on the latest matrix Perl version and
ubuntu-latest. The step installs App::perlimports from CPAN and runsperlimports --lintacross all.pmfiles underlib/. It is markedcontinue-on-error: truebecause import hygiene warnings are advisory; they should not block a CI run in established codebases.
Generated step order:
- 1.
actions/checkout - 2. Setup Perl via
shogo82148/actions-setup-perl - 3. Cache CPAN modules via
actions/cache - 4. Install cpanm and
local::lib - 5. Install project dependencies
- 6. Lint and syntax check — all matrix cells (when
enable_linteris true); the unused-variable check viaPERL5OPT=-Mwarnings::unusedis embedded at the end of this step whenenable_linter_unusedis true - 7. Run tests
- 8. Run Perl::Critic — latest Perl + Ubuntu only (when
enable_criticis true) - 9. Check imports with perlimports — latest Perl + Ubuntu only (when
enable_perlimportsis true) - 10. Test coverage — latest Perl + Ubuntu only (when
enable_coverageis true) - 11. Show cpanm build log on failure
API SPECIFICATION
Input
{
opts => {
type => 'hashref',
default => {},
keys => {
perl_versions => { type => 'arrayref', optional => 1 },
min_perl_version => { type => 'scalar', default => '5.36' },
max_perl_version => { type => 'scalar', default => '5.42' },
os => { type => 'arrayref', optional => 1 },
timeout => { type => 'scalar', default => 30 },
enable_linter => { type => 'scalar', default => 1 },
enable_linter_unused => { type => 'scalar', default => 1 },
enable_critic => { type => 'scalar', default => 1 },
enable_coverage => { type => 'scalar', default => 1 },
enable_perlimports => { type => 'scalar', default => 1 },
},
},
}
Output
{ type => 'scalar' } # multi-line YAML string starting with '---'
FORMAL SPECIFICATION
generate_custom_perl_workflow : Opts → YAML
versions ≔ opts.perl_versions ?? range(min, max)
latest ≔ versions[|versions|−1]
yaml contains "Lint and syntax check" step ↔ opts.enable_linter = 1
yaml contains "PERL5OPT=-Mwarnings::unused" ↔ opts.enable_linter_unused = 1
yaml contains "Run Perl::Critic" ↔ opts.enable_critic = 1
yaml contains "Test coverage" ↔ opts.enable_coverage = 1
yaml contains "Check imports with perlimports" ↔ opts.enable_perlimports = 1
step ordering invariant:
pos(lint) < pos(unused) < pos(tests) < pos(critic) < pos(perlimports) < pos(coverage)
LIMITATIONS
Windows + Perl 5.40 excluded from the generated matrix
The shogo82148 perl-5.40.4-thr-win32-x64.zip distribution ships a perl.exe and a libperl540.a from two different builds. Any XS module compiled against that libperl540.a receives handshake key 0x12c00080, but perl.exe needs 0x12d00080. Even Win32::Process — a core Windows XS module bundled in the zip and loaded transitively by IPC::System::Simple — fails to load with this mismatch, so no XS-using test can pass on this combination.
Recompiling XS modules (e.g. via cpanm --reinstall YAML::XS) does not help: the compilation itself links against the broken libperl540.a and inherits the wrong key.
The generated workflow therefore includes a matrix.exclude entry for {os: windows-latest, perl: '5.40'}. This entry is only emitted when both windows-latest and 5.40 are present in the matrix, so workflows that restrict their OS or Perl lists are unaffected.
Remove the exclusion once the upstream shogo82148/build-perl distribution is fixed and the perl.exe and libperl540.a in the 5.40.x zip are built from the same source tree.
See https://github.com/shogo82148/actions-setup-perl/issues/2310 for further information.
AUTHOR
Nigel Horne <njh@nigelhorne.com>
LICENSE
Usage is subject to the GPL2 licence terms. If you use it, please let me know.