aboutsummaryrefslogtreecommitdiff
path: root/test/lint/lint-shell.py
blob: 7fb78894af3e6bef833f912bf80c1f82e09b11ce (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#!/usr/bin/env python3
#
# Copyright (c) 2018-2022 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.

"""
Check for shellcheck warnings in shell scripts.
"""

import subprocess
import re
import sys

# Disabled warnings:
DISABLED = [
    'SC2162', # read without -r will mangle backslashes.
]

def check_shellcheck_install():
    try:
        subprocess.run(['shellcheck', '--version'], stdout=subprocess.DEVNULL, check=True)
    except FileNotFoundError:
        print('Skipping shell linting since shellcheck is not installed.')
        sys.exit(0)

def get_files(command):
    output = subprocess.run(command, stdout=subprocess.PIPE, text=True)
    files = output.stdout.split('\n')

    # remove whitespace element
    files = list(filter(None, files))
    return files

def main():
    check_shellcheck_install()

    # build the `exclude` flag
    exclude = '--exclude=' + ','.join(DISABLED)

    # build the `sourced files` list
    sourced_files_cmd = [
        'git',
        'grep',
        '-El',
        r'^# shellcheck shell=',
    ]
    sourced_files = get_files(sourced_files_cmd)

    # build the `guix files` list
    guix_files_cmd = [
        'git',
        'grep',
        '-El',
        r'^#!\/usr\/bin\/env bash',
        '--',
        'contrib/guix',
        'contrib/shell',
    ]
    guix_files = get_files(guix_files_cmd)

    # build the other script files list
    files_cmd = [
        'git',
        'ls-files',
        '--',
        '*.sh',
    ]
    files = get_files(files_cmd)
    reg = re.compile(r'src/[leveldb,secp256k1,minisketch]')

    def should_exclude(fname: str) -> bool:
        return bool(reg.match(fname))

    # remove everything that doesn't match this regex
    files[:] = [file for file in files if not should_exclude(file)]

    # build the `shellcheck` command
    shellcheck_cmd = [
        'shellcheck',
        '--external-sources',
        '--check-sourced',
        '--source-path=SCRIPTDIR',
    ]
    shellcheck_cmd.append(exclude)
    shellcheck_cmd.extend(sourced_files)
    shellcheck_cmd.extend(guix_files)
    shellcheck_cmd.extend(files)

    # run the `shellcheck` command
    try:
        subprocess.check_call(shellcheck_cmd)
    except subprocess.CalledProcessError:
        sys.exit(1)

if __name__ == '__main__':
    main()