name: 'struct-audit'
description: 'Analyze binary memory layouts to detect padding inefficiencies'
author: 'avifen'
branding:
icon: 'cpu'
color: 'orange'
inputs:
binary:
description: 'Path to the binary file to analyze'
required: true
command:
description: 'Command to run: inspect, diff, or check'
required: false
default: 'inspect'
baseline:
description: 'Path to baseline binary (for diff command)'
required: false
config:
description: 'Path to config file (for check command)'
required: false
default: '.struct-audit.yaml'
filter:
description: 'Filter structs by name (substring match)'
required: false
output:
description: 'Output format: table or json'
required: false
default: 'table'
sort-by:
description: 'Sort by: name, size, padding, or padding-pct'
required: false
default: 'padding'
top:
description: 'Show only top N structs'
required: false
min-padding:
description: 'Show only structs with at least N bytes of padding'
required: false
fail-on-regression:
description: 'Fail if size or padding increased (for diff command)'
required: false
default: 'false'
version:
description: 'Version of struct-audit to use'
required: false
default: 'latest'
outputs:
report:
description: 'The struct-audit output'
value: ${{ steps.run.outputs.report }}
runs:
using: 'composite'
steps:
- name: Install struct-audit
shell: bash
run: |
if [ "${{ inputs.version }}" = "latest" ]; then
cargo install struct-audit
else
cargo install struct-audit --version ${{ inputs.version }}
fi
- name: Run struct-audit
id: run
shell: bash
env:
INPUT_COMMAND: ${{ inputs.command }}
INPUT_BINARY: ${{ inputs.binary }}
INPUT_BASELINE: ${{ inputs.baseline }}
INPUT_CONFIG: ${{ inputs.config }}
INPUT_FILTER: ${{ inputs.filter }}
INPUT_OUTPUT: ${{ inputs.output }}
INPUT_SORT_BY: ${{ inputs.sort-by }}
INPUT_TOP: ${{ inputs.top }}
INPUT_MIN_PADDING: ${{ inputs.min-padding }}
INPUT_FAIL_ON_REGRESSION: ${{ inputs.fail-on-regression }}
run: |
set +e
args=()
case "$INPUT_COMMAND" in
inspect)
args+=(inspect "$INPUT_BINARY")
if [ -n "$INPUT_FILTER" ]; then
args+=(--filter "$INPUT_FILTER")
fi
args+=(--output "$INPUT_OUTPUT")
args+=(--sort-by "$INPUT_SORT_BY")
if [ -n "$INPUT_TOP" ]; then
args+=(--top "$INPUT_TOP")
fi
if [ -n "$INPUT_MIN_PADDING" ]; then
args+=(--min-padding "$INPUT_MIN_PADDING")
fi
;;
diff)
if [ -z "$INPUT_BASELINE" ]; then
echo "Error: baseline input is required for diff command"
exit 1
fi
args+=(diff "$INPUT_BASELINE" "$INPUT_BINARY")
if [ -n "$INPUT_FILTER" ]; then
args+=(--filter "$INPUT_FILTER")
fi
args+=(--output "$INPUT_OUTPUT")
if [ "$INPUT_FAIL_ON_REGRESSION" = "true" ]; then
args+=(--fail-on-regression)
fi
;;
check)
args+=(check "$INPUT_BINARY" --config "$INPUT_CONFIG")
;;
*)
echo "Error: unknown command '$INPUT_COMMAND'"
exit 1
;;
esac
echo "Running: struct-audit ${args[*]}"
OUTPUT=$(struct-audit "${args[@]}" 2>&1)
EXIT_CODE=$?
echo "$OUTPUT"
# Set output for use in subsequent steps
{
echo "report<<EOF"
echo "$OUTPUT"
echo "EOF"
} >> "$GITHUB_OUTPUT"
exit $EXIT_CODE