Coder Social home page Coder Social logo

mschuett / yaml-shellcheck Goto Github PK

View Code? Open in Web Editor NEW
15.0 4.0 2.0 69 KB

Wrapper script to run shellcheck on YAML CI-config files

License: BSD 2-Clause "Simplified" License

Python 97.71% Dockerfile 2.29%
shellcheck shell gitlab-ci bitbucket-pipelines ci-cd static-analysis linter

yaml-shellcheck's Introduction

yaml-shellcheck

Docker Repository on Quay

Wrapper script to run shellcheck on YAML CI-config files. Currently supported formats are Bitbucket Pipelines, GitLab CI, GitHub Actions, Drone CI, CircleCI, and (very limited) Ansible

Usage

Shell

Needs Python 3 with library ruamel.yaml, and shellcheck.

$ ./yaml_shellcheck.py -h
usage: yaml_shellcheck.py [-h] [-o OUTDIR] [-k] [-d] [-s SHELL] [-c COMMAND] files [files ...]

run shellcheck on script blocks from .gitlab-ci.yml or bitbucket-pipelines.yml

positional arguments:
  files                 YAML files to read

optional arguments:
  -h, --help            show this help message and exit
  -o OUTDIR, --outdir OUTDIR
                        output directory (default: create temporary directory)
  -k, --keep            keep (do not delete) output directory
  -d, --debug           debug output
  -s SHELL, --shell SHELL
                        default shebang line to add to shell script snippets (default: '#!/bin/sh -e')
  -c COMMAND, --command COMMAND
                        shellcheck command to run (default: shellcheck)

Exit Status/Return Values

The tool exits with the exit code from shellcheck, from their man page:

ShellCheck uses the follow exit codes:
• 0: All files successfully scanned with no issues.
• 1: All files successfully scanned with some issues.
• 2: Some files could not be processed (e.g. file not found).
• 3: ShellCheck was invoked with bad syntax (e.g. unknown flag).
• 4: ShellCheck was invoked with bad options (e.g. unknown formatter).

Docker

# build image
$ docker build . -t yaml_shellcheck:latest
# run image
$ docker run -v `pwd`:/app yaml_shellcheck app/*.yaml

GitLab CI

lint_yaml_shellcheck:
  image:
    name: quay.io/mschuette/yaml-shellcheck:latest
    entrypoint: [""]
  script:
    - find . -name \*.yaml -or -name \*.yml | xargs python3 yaml_shellcheck.py

File Formats

The main function of this tool is to encode which elements inside the YAML data contain shell scripts. So far three formats are supported.

Bitbucket Pipelines

Handling Bitbucket files is very simple. A file with a pipelines object is read as a Bitbucket Pipeline file, and every script attribute inside is considered a shell script.

GitHub Actions & Forgejo Actions

GitHub Actions are similar to Bitbucket. A file with a jobs object is read as a GitHub Actions file, and every run attribute inside is considered a shell script.

As far as I can tell Forgejo Actions (as used e.g. by https://codeberg.org/) intentionally use the same structure as GitHub Actions, so these are covered here as well.

  • shell attributes are not supported
    this is a todo, it should be simple enough to only check sh and bash scripts with right shebang line
  • expressions get replaced with a simple variable before running shellcheck

Drone CI

Drone CI has a very simple file structure. As far as I can tell it only has conditions, but no deep nesting or includes. All command lists are concatenated and checked as a shell script.

CircleCI

CircleCI has many options, but fortunately the nesting of its jobs.*.steps.run script elements is straightforward. All command lists are concatenated and checked as a shell script.

GitLab CI Pipelines

GitLab CI files have more structure, and we try to support more of it.

  • script, before_script, after_script: GitLab has not one, but three different shell script attributes which are read as three independent scripts. In GitLab before_script and script get concatenated and executed in a single shell process, whereas after_script always starts a new shell process. This has some implications for variable visibility etc. and is ignored in this tool.
  • include is not supported, every YAML file is parsed on its own. For large pipelines with many includes you may want to use other tools to resolve all include and then run yaml_shellcheck on the merged YAML file.
  • !reference is semi-supported, we only read the tag and insert a placeholder in order not to break the YAML parsing (todo, should be simple to improve).
  • decided to not support variables checking. shellcheck checks lower-case variables for assignments before usage, but it assumes all upper-case variable names are externally provided. -- IMHO GitLab CI jobs should follow that convention and use upper-case variable names for CI/CD variables.

Ansible

Ansible support is limited. This tool reads Ansible playbook or task files with YAML lists.

So far it recognizes three kinds of list elements:

  • Tasks using the shell (or ansible.builtin.shell) module.
  • Playbook elements, containing a tasks attribute.
  • Blocks (block) for nested task lists.
  • Jinja expressions ({{ ... }}) are ignored and replaced with placeholder shell variables.

Common

Files are read with a YAML parser, so all YAML anchors are resolved. There is no additional check on data types or structure.

Following the Bitbucket/GitLab usage a script block may contain a string or an array of strings.

yaml-shellcheck's People

Contributors

mschuett avatar temp20230620 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

yaml-shellcheck's Issues

Bug: using absolute paths throws FileExistsError

$ docker run -v `pwd`:/app2 --rm quay.io/mschuette/yaml-shellcheck /app2/test-input/github-workflow.yml

2024-08-14 02:18:05,957 - root - INFO - read /app2/test-input/github-workflow.yml as GitHub Actions config...
Traceback (most recent call last):
  File "/app/yaml_shellcheck.py", line 459, in <module>
    main()
  File "/app/yaml_shellcheck.py", line 448, in main
    filenames.extend(write_tmp_files(args, result))
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/yaml_shellcheck.py", line 402, in write_tmp_files
    subdir.mkdir(exist_ok=True, parents=True)
  File "/usr/local/lib/python3.12/pathlib.py", line 1311, in mkdir
    os.mkdir(self, mode)
FileExistsError: [Errno 17] File exists: '/app2/test-input/github-workflow.yml'

or

$ docker run -v `pwd`:`pwd` --rm quay.io/mschuette/yaml-shellcheck  `pwd`/**/*.y*ml
2024-08-14 02:32:29,902 - root - INFO - read /home/luka/Downloads/yaml-shellcheck/test-input/ansible-playbook.yml as Ansible file...
Traceback (most recent call last):
  File "/app/yaml_shellcheck.py", line 459, in <module>
    main()
  File "/app/yaml_shellcheck.py", line 448, in main
    filenames.extend(write_tmp_files(args, result))
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/yaml_shellcheck.py", line 402, in write_tmp_files
    subdir.mkdir(exist_ok=True, parents=True)
  File "/usr/local/lib/python3.12/pathlib.py", line 1311, in mkdir
    os.mkdir(self, mode)
FileExistsError: [Errno 17] File exists: '/home/luka/Downloads/yaml-shellcheck/test-input/ansible-playbook.yml'

This happens because

subdir = outdir / filename

is just subdir = filename for absolute paths.

Naive fix

lukaw3d@8df5d64

$ docker run -v `pwd`:/app2 --rm ghcr.io/lukaw3d/yaml-shellcheck:main /app2/test-input/github-workflow.yml
2024-08-14 02:29:29,192 - root - INFO - read /app2/test-input/github-workflow.yml as GitHub Actions config...

In a/app2/test-input/github-workflow.yml/jobs/hello_world_job/steps/2/run line 2:
echo random-number $ACTION_EXPRESSION
                   ^----------------^ SC2086 (info): Double quote to prevent globbing and word splitting.

Did you mean: 
echo random-number "$ACTION_EXPRESSION"

For more information:
  https://www.shellcheck.net/wiki/SC2086 -- Double quote to prevent globbing ...

Gitlab spec:inputs are breaking the script

I am using this for linting in some Gitlab pipelines and just noticed that on newer jobs which use spec:inputs it's breaking since it's technically two YAML files in one.

More information about spec:inputs:
https://docs.gitlab.com/ee/ci/yaml/inputs.html

Error:

Traceback (most recent call last):
  File "/root/yaml-shellcheck/yaml_shellcheck.py", line 440, in <module>
    main()
  File "/root/yaml-shellcheck/yaml_shellcheck.py", line 427, in main
    result = {filename: read_yaml_file(filename)}
  File "/root/yaml-shellcheck/yaml_shellcheck.py", line 368, in read_yaml_file
    data = yaml.load(f)
  File "/usr/local/lib/python3.9/site-packages/ruamel/yaml/main.py", line 451, in load
    return constructor.get_single_data()
  File "/usr/local/lib/python3.9/site-packages/ruamel/yaml/constructor.py", line 114, in get_single_data
    node = self.composer.get_single_node()
  File "_ruamel_yaml.pyx", line 718, in ruamel.yaml.clib._ruamel_yaml.CParser.get_single_node
ruamel.yaml.composer.ComposerError: expected a single document in the stream
  in "./Jobs/Trigger.gitlab-ci.yml", line 13, column 1
but found another document
  in "./Jobs/Trigger.gitlab-ci.yml", line 52, column 1

Very cut down example pipeline:

spec:
  inputs:
    target:
      type: string
      description: The Target URL
---
trigger:
  stage: .post
  image: $CI_REGISTRY/pipeline-components/helperimages:alpine-utils
  script:
    - |
      curl --location $[[ inputs.target ]]

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.