Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .pre-commit-hooks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,9 @@
- manual
types:
- yaml
- id: shellcheck-run-steps
name: shellcheck run steps
description: run shellcheck on each "run" step in a melange pipeline
entry: shellcheck-run-steps
language: python
types: [yaml]
Empty file added pre_commit_hooks/__init__.py
Empty file.
86 changes: 86 additions & 0 deletions pre_commit_hooks/shellcheck_run_steps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
from __future__ import annotations

import argparse
import subprocess
import tempfile
from collections.abc import Generator
from collections.abc import Sequence
from typing import Any
from typing import NamedTuple

import ruamel.yaml

yaml = ruamel.yaml.YAML(typ='safe')


def _exhaust(gen: Generator[str]) -> None:
for _ in gen:
pass


def _parse_unsafe(*args: Any, **kwargs: Any) -> None:
_exhaust(yaml.parse(*args, **kwargs))


def _load_all(*args: Any, **kwargs: Any) -> None:
_exhaust(yaml.load_all(*args, **kwargs))


class Key(NamedTuple):
multi: bool
unsafe: bool


def do_shellcheck(melange_cfg):
if melange_cfg == {}:
return 0

pkgs = [melange_cfg]
pkgs.extend(melange_cfg.get('subpackages', []))
pipelines = []
for pkg in pkgs:
pipelines.extend(pkg.get('pipeline', []))
if 'test' in pkg.keys():
test_pipeline = pkg['test'].get('pipeline', [])
pipelines.extend(test_pipeline)

for step in pipelines:
if 'runs' not in step.keys():
continue
with tempfile.NamedTemporaryFile(mode='w') as shfile:
shfile.write(step['runs'])
subprocess.check_call(
['shellcheck', '--shell=busybox', shfile.name]
)

def main(argv: Sequence[str] | None = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument('filenames', nargs='*', help='Filenames to check.')
args = parser.parse_args(argv)

melange_cfg = {}
for filename in args.filenames:
with tempfile.NamedTemporaryFile(
'w', delete_on_close=False
) as compiled_out:
subprocess.check_call(
[
'melange', 'compile', '--arch', 'x86_64',
'--pipeline-dir', './pipelines', filename,
],
stdout=compiled_out,
)
compiled_out.close()
try:
with open(compiled_out.name, 'r') as compiled_in:
melange_cfg = yaml.load(compiled_in)
do_shellcheck(melange_cfg)
except ruamel.yaml.YAMLError as exc:
print(exc)
return 1

return 0


if __name__ == '__main__':
raise SystemExit(main())
44 changes: 44 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
[metadata]
name = pre_commit_hooks
version = 0.0.1
description = chainguard hooks for pre-commit
long_description = file: README.md
long_description_content_type = text/markdown
url = https://github.com/chainguard-dev/pre-commit-hooks
license = MIT
license_files = LICENSE
classifiers =
Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: Implementation :: CPython
Programming Language :: Python :: Implementation :: PyPy

[options]
packages = find:
install_requires =
ruamel.yaml>=0.15
python_requires = >=3.9

[options.entry_points]
console_scripts =
shellcheck-run-steps = pre_commit_hooks.shellcheck_run_steps:main

[bdist_wheel]
universal = True

[coverage:run]
plugins = covdefaults

[mypy]
check_untyped_defs = true
disallow_any_generics = true
disallow_incomplete_defs = true
disallow_untyped_defs = true
warn_redundant_casts = true
warn_unused_ignores = true

[mypy-testing.*]
disallow_untyped_defs = false

[mypy-tests.*]
disallow_untyped_defs = false
4 changes: 4 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from __future__ import annotations

from setuptools import setup
setup()