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 over min_perl_version and max_perl_version when supplied.

min_perl_version (string, default '5.36')

Lowest Perl version to include in the matrix when perl_versions is 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_versions is not given. The built-in version table currently spans 5.22 through 5.44; the default produces the matrix 5.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 set perl_versions explicitly:

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, default 30)

Value written to timeout-minutes on every matrix job.

enable_linter (boolean, default 1)

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 searches lib/ and bin/ (falling back to . when neither exists), spawns perl -Mstrict -Mwarnings -c for each .pm file found, and exits non-zero if any file fails. No additional CPAN modules are required.

enable_linter_unused (boolean, default 1)

When true, inserts a "Check for unused variables" step immediately after the test run and before Perl::Critic. The step installs warnings::unused from CPAN and runs perl -Mwarnings::unused -c on every .pm file under lib/.

This step is conditioned on the latest matrix Perl version and ubuntu-latest, matching the Perl::Critic and coverage steps. It is also marked continue-on-error: true because unused-variable warnings can be legitimately noisy on some codebases.

enable_critic (boolean, default 1)

When true, adds a Perl::Critic step on the latest matrix Perl version and ubuntu-latest.

enable_coverage (boolean, default 1)

When true, adds a Devel::Cover test-coverage step on the latest matrix Perl version and ubuntu-latest.

enable_perlimports (boolean, default 1)

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 runs perlimports --lint across all .pm files under lib/. It is marked continue-on-error: true because 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_linter is true); the unused-variable check via PERL5OPT=-Mwarnings::unused is embedded at the end of this step when enable_linter_unused is true
7. Run tests
8. Run Perl::Critic — latest Perl + Ubuntu only (when enable_critic is true)
9. Check imports with perlimports — latest Perl + Ubuntu only (when enable_perlimports is true)
10. Test coverage — latest Perl + Ubuntu only (when enable_coverage is 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)

AUTHOR

Nigel Horne <njh@nigelhorne.com>

https://github.com/nigelhorne

LICENSE

Usage is subject to the GPL2 licence terms. If you use it, please let me know.