NAME
App::GHGen::Analyzer - Analyze GitHub Actions workflows
SYNOPSIS
use App::GHGen::Analyzer qw(analyze_workflow);
my @issues = analyze_workflow($workflow_hashref, 'ci.yml');
FUNCTIONS
find_workflows()
Find all workflow files in .github/workflows directory. Returns a sorted list of Path::Tiny objects.
Purpose
Discover every GitHub Actions workflow file under .github/workflows in the current working directory.
Arguments
None.
Returns
A sorted list of Path::Tiny objects, one per .yml or .yaml file found. Returns an empty list when the directory does not exist or is empty.
Side Effects
None. Performs read-only filesystem access.
Usage Example
use App::GHGen::Analyzer qw(find_workflows);
chdir '/path/to/repo';
my @wfs = find_workflows();
say $_->basename for @wfs;
API SPECIFICATION
Input
# No parameters.
Output
{ type => 'array', element => { isa => 'Path::Tiny' } }
FORMAL SPECIFICATION
FindWorkflows : → seq Path
dir ≔ .github/workflows
FindWorkflows ≡
¬dir.exists ∨ ¬dir.is_dir → ⟨⟩
| otherwise → sort(lex) { f ∈ dir.children ∣ f.name =~ /\.ya?ml$/i }
analyze_workflow($workflow, $filename)
Analyze a workflow hash for issues. Returns array of issue hashes.
Each issue has: type, severity, message, and an optional fix.
Purpose
Inspect a parsed GitHub Actions workflow and return a list of detected issues covering performance, security, cost, and maintenance concerns.
Arguments
$workflow(HashRef, required)-
A hash reference representing the parsed workflow (e.g. from
YAML::XS::LoadFile). Must contain at least ajobskey whose value is a hash of job definitions. $filename(Str, required)-
The filename of the workflow, used only as context in issue messages.
Returns
A list (not an array reference) of issue hash references. Each issue has:
{
type => Str, # 'performance' | 'security' | 'cost' | 'maintenance'
severity => Str, # 'high' | 'medium' | 'low'
message => Str,
fix => Str, # optional: suggested YAML snippet
}
Returns an empty list when no issues are detected.
Side Effects
None. Pure function; does not modify its arguments.
Usage Example
use App::GHGen::Analyzer qw(analyze_workflow);
use YAML::XS qw(LoadFile);
my $wf = LoadFile('.github/workflows/ci.yml');
my @issues = analyze_workflow($wf, 'ci.yml');
for my $issue (@issues) {
say "$issue->{severity}: $issue->{message}";
}
API SPECIFICATION
Input
{
workflow => { type => 'hashref', required => 1 },
filename => { type => 'scalar', required => 1 },
}
Output
{
type => 'array',
element => {
type => 'hashref',
keys => {
type => { type => 'scalar' },
severity => { type => 'scalar' },
message => { type => 'scalar' },
fix => { type => 'scalar', optional => 1 },
},
},
}
FORMAL SPECIFICATION
Issue ≔ { type: IssueType, severity: Severity, message: ℤ*, fix?: ℤ* }
IssueType ≔ performance | security | cost | maintenance
Severity ≔ high | medium | low
analyze_workflow : Workflow × ℤ* → seq Issue
∀ w: Workflow, f: ℤ*:
¬has_caching(w) ⇒ performance∈medium ∈ result(w,f)
find_unpinned_actions(w)≠∅ ⇒ security∈high ∈ result(w,f)
find_outdated_actions(w)≠∅ ⇒ maintenance∈medium ∈ result(w,f)
has_broad_triggers(w) ⇒ cost∈medium ∈ result(w,f)
¬w.concurrency ⇒ cost∈low ∈ result(w,f)
has_outdated_runners(w) ⇒ maintenance∈low ∈ result(w,f)
∃ j∈w.jobs: ¬j.timeout-minutes ⇒ performance∈low ∈ result(w,f)
get_cache_suggestion($workflow)
Generate a caching suggestion based on detected project type.
Purpose
Inspect a workflow for known package-manager commands and return a ready-to-paste actions/cache YAML snippet that matches the detected ecosystem.
Arguments
$workflow(HashRef, required)-
A parsed workflow hash. The function inspects each job's
steps[].runcommands to detectnpm,pip,cargo, orbundle.
Returns
A non-empty string. When the ecosystem is recognised the string is an actions/cache YAML step snippet; otherwise a generic guidance message.
Side Effects
None. Pure function.
Usage Example
my $suggestion = get_cache_suggestion($workflow);
say $suggestion;
API SPECIFICATION
Input
{ workflow => { type => 'hashref', required => 1 } }
Output
{ type => 'scalar' }
FORMAL SPECIFICATION
Ecosystem ≔ npm | pip | cargo | bundler | unknown
get_cache_suggestion : Workflow → ℤ*
ecosystem(w) = npm ⇒ result contains ~/.npm cache path
ecosystem(w) = pip ⇒ result contains ~/.cache/pip path
ecosystem(w) = cargo ⇒ result contains ~/.cargo path
ecosystem(w) = bundler ⇒ result contains vendor/bundle path
ecosystem(w) = unknown ⇒ result is a generic guidance message
AUTHOR
Nigel Horne <njh@nigelhorne.com>
LICENCE
Copyright 2025-2026 Nigel Horne.
Usage is subject to license terms.
1 POD Error
The following errors were encountered while parsing the POD:
- Around line 73:
Non-ASCII character seen before =encoding in '→'. Assuming UTF-8