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
find_outdated_actions($workflow)
Return a list of human-readable upgrade notices for any action whose version string is a key in App::GHGen::Fixer::ACTION_UPDATES.
Purpose
Detect uses: entries that reference a known-outdated action version. The detection set is derived directly from %ACTION_UPDATES in App::GHGen::Fixer, so it is always in sync with what update_actions can fix: every action flagged here can be auto-fixed, and every action the Fixer knows about is flagged here.
Arguments
Returns
A list of strings of the form "old/action\@vN → new/action\@vM", one per outdated step found. Returns an empty list when none are detected.
Side Effects
None. Pure function.
FORMAL SPECIFICATION
find_outdated_actions : Workflow → seq ℤ*
outdated ≔ { "$old → $new" ∣ job ∈ w.jobs, step ∈ job.steps,
old ∈ dom(%ACTION_UPDATES), step.uses =~ /^\Qold\E/ }
result ≔ seq(outdated)
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 74:
Non-ASCII character seen before =encoding in '→'. Assuming UTF-8