Embedded Scripts
shuck check can lint shell embedded in supported non-shell files, starting with GitHub Actions workflows and composite actions.
Supported files
.github/workflows/*.yml.github/workflows/*.yamlaction.ymlaction.yaml
Each GitHub Actions run: block is extracted and linted as its own shell script.
How it works
When Shuck analyzes one of those files, it:
- Resolves the effective shell from
shell:,defaults.run.shell, and job defaults. - Lints supported shells such as
bashandsh. - Skips unsupported shells such as PowerShell,
cmd, andpython. - Replaces
${{ ... }}expressions with synthetic placeholders so the shell parser sees valid shell syntax. - Remaps diagnostics back to the original YAML file and includes the workflow step path in the message.
That means you can lint GitHub Actions directly without copying run: blocks into temporary .sh files.
Example
on:
issues:
types: [opened]
jobs:
triage:
runs-on: ubuntu-latest
steps:
- name: Build summary
run: |
summary="${{ github.event.issue.title }}"shuck check --output-format concise .github/workflows/issues.yml.github/workflows/issues.yml:10:11: warning[C001] jobs.triage.steps[0].run: variable `summary` is assigned but never usedSuppressions
For embedded scripts, suppression comments must live inside the run: block as shell comments:
- run: |
# shellcheck disable=SC2086
echo $FOOYAML comments outside the scalar are not part of the extracted shell script, so they do not suppress shell diagnostics.
Configuration
Embedded-script extraction is enabled by default. You can control it with the [check] section in shuck.toml or .shuck.toml:
[check]
embedded = trueSet embedded = false to lint only standalone shell files.