diff --git a/.github/actions/setup-actionlint/action.yml b/.github/actions/setup-actionlint/action.yml new file mode 100644 index 00000000..425669d9 --- /dev/null +++ b/.github/actions/setup-actionlint/action.yml @@ -0,0 +1,29 @@ +--- +name: setup-actionlint +description: Setup actionlint +inputs: + version: + description: The version of actionlint + default: 1.6.25 + +runs: + using: composite + steps: + + - name: Cache actionlint Binary + uses: actions/cache@v3 + with: + path: /usr/local/bin/actionlint + key: ${{ runner.os }}|${{ runner.arch }}|actionlint|${{ inputs.version }} + + - name: Setup actionlint + shell: bash + run: | + if ! command -v actionlint; then + bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) ${{ inputs.version }} + mv ./actionlint /usr/local/bin/actionlint + fi + - name: Show actionlint Version + shell: bash + run: | + actionlint --version diff --git a/.github/actions/setup-shellcheck/action.yml b/.github/actions/setup-shellcheck/action.yml new file mode 100644 index 00000000..986404f2 --- /dev/null +++ b/.github/actions/setup-shellcheck/action.yml @@ -0,0 +1,31 @@ +--- +name: setup-shellcheck +description: Setup shellcheck +inputs: + version: + description: The version of shellcheck + default: v0.9.0 + +runs: + using: composite + steps: + + - name: Cache shellcheck Binary + uses: actions/cache@v3 + with: + path: /usr/local/bin/shellcheck + key: ${{ runner.os }}|${{ runner.arch }}|shellcheck|${{ inputs.version }} + + - name: Setup shellcheck + shell: bash + run: | + if ! command -v shellcheck; then + wget https://github.com/koalaman/shellcheck/releases/download/${{ inputs.version }}/shellcheck-${{ inputs.version }}.${{ runner.os }}.x86_64.tar.xz + tar xf shellcheck-${{ inputs.version }}.${{ runner.os }}.x86_64.tar.xz + mv shellcheck-${{ inputs.version }}/shellcheck /usr/local/bin/shellcheck + rm -rf shellcheck-${{ inputs.version }}.${{ runner.os }}.x86_64.tar.xz shellcheck-${{ inputs.version }} + fi + - name: Show shellcheck Version + shell: bash + run: | + shellcheck --version diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 20e8f253..e227b2f9 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -21,12 +21,14 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 + - uses: ./.github/actions/setup-actionlint + - uses: ./.github/actions/setup-shellcheck - name: Set up Python uses: actions/setup-python@v4 with: python-version: 3.9 - name: Set Cache Key - run: echo "PY=$(python --version --version | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV + run: echo "PY=$(python --version --version | sha256sum | cut -d' ' -f1)" >> "$GITHUB_ENV" - uses: actions/cache@v3 with: path: ~/.cache/pre-commit @@ -60,11 +62,6 @@ jobs: python -m pip install --upgrade pip python -m pip install nox - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - name: Install Doc Requirements run: | nox --force-color -e docs --install-only @@ -117,6 +114,8 @@ jobs: matrix: pytest-version: - "7.3.0" + salt-version: + - "3005" steps: - uses: actions/checkout@v3 @@ -134,9 +133,9 @@ jobs: - name: Install Salt run: | - curl -L https://bootstrap.saltstack.com | sudo sh -s -- -M -X -x python3 old-stable 3005 + curl -L https://bootstrap.saltstack.com | sudo sh -s -- -M -X -x python3 ${{ matrix.salt-version == '3005' && 'old-stable' || 'stable' }} ${{ matrix.salt-version }} sudo apt-get install -y salt-api salt-ssh salt-syndic salt-cloud python3-pip - for service in $(sudo systemctl list-unit-files | grep salt | grep -v @ | awk '{ print $1 }'); do sudo systemctl stop $service; done + for service in $(sudo systemctl list-unit-files | grep salt | grep -v @ | awk '{ print $1 }'); do sudo systemctl stop "$service"; done - name: Install Test Requirements run: | @@ -156,8 +155,8 @@ jobs: directory: artifacts/ fail_ci_if_error: false files: coverage-project.xml - flags: src,${{ runner.os }},salt-${{ matrix.salt-version }},py${{ matrix.python-version }},pytest-${{ matrix.pytest-version }} - name: project-${{ runner.os }}-Salt-${{ matrix.salt-version }}-Py${{ matrix.python-version}}-Pytest${{ matrix.pytest-version }} + flags: src,${{ runner.os }},salt-${{ matrix.salt-version }},system,pytest-${{ matrix.pytest-version }} + name: project-${{ runner.os }}-Salt-${{ matrix.salt-version }}-System-Pytest${{ matrix.pytest-version }} verbose: true - name: Upload coverage to Codecov @@ -168,15 +167,15 @@ jobs: env_vars: PYTHON,SALT,TESTSUITE,SOURCE fail_ci_if_error: false files: coverage-tests.xml - flags: tests,${{ runner.os }},salt-${{ matrix.salt-version }},py${{ matrix.python-version }},pytest-${{ matrix.pytest-version }} - name: tests-${{ runner.os }}-Salt-${{ matrix.salt-version }}-Py${{ matrix.python-version}}-Pytest${{ matrix.pytest-version }} + flags: tests,${{ runner.os }},salt-${{ matrix.salt-version }},system,pytest-${{ matrix.pytest-version }} + name: tests-${{ runner.os }}-Salt-${{ matrix.salt-version }}-System-Pytest${{ matrix.pytest-version }} verbose: true - name: Upload Logs if: always() uses: actions/upload-artifact@v3 with: - name: runtests-${{ steps.codecov.outputs.report-name }}-system.log + name: runtests-${{ runner.os }}-Salt-${{ matrix.salt-version }}-System-Pytest${{ matrix.pytest-version }}.log path: artifacts/runtests-*.log @@ -262,7 +261,7 @@ jobs: if: always() uses: actions/upload-artifact@v3 with: - name: runtests-${{ steps.codecov.outputs.report-name }}.log + name: runtests-${{ runner.os }}-Salt-${{ matrix.salt-version }}-Py${{ matrix.python-version}}-Pytest${{ matrix.pytest-version }}.log path: artifacts/runtests-*.log @@ -343,7 +342,7 @@ jobs: if: always() uses: actions/upload-artifact@v3 with: - name: runtests-${{ steps.codecov.outputs.report-name }}.log + name: runtests-${{ runner.os }}-Salt-${{ matrix.salt-version }}-Py${{ matrix.python-version}}-Pytest${{ matrix.pytest-version }}.log path: artifacts/runtests-*.log @@ -429,7 +428,7 @@ jobs: if: always() uses: actions/upload-artifact@v3 with: - name: runtests-${{ steps.codecov.outputs.report-name }}.log + name: runtests-${{ runner.os }}-Salt-${{ matrix.salt-version }}-Py${{ matrix.python-version}}-Pytest${{ matrix.pytest-version }}.log path: artifacts/runtests-*.log @@ -443,7 +442,7 @@ jobs: - PyLint - Linux - Windows - - macOS + - MacOS - Linux-System-Service steps: - uses: actions/checkout@v3 @@ -470,3 +469,37 @@ jobs: print-hash: true skip-existing: true verify-metadata: true + + set-pipeline-exit-status: + # This step is just so we can make github require this step, to pass checks + # on a pull request instead of requiring all + name: Set the ${{ github.workflow }} Pipeline Exit Status + if: always() + runs-on: ubuntu-latest + needs: + - pre-commit + - Docs + - PyLint + - Linux + - Windows + - MacOS + - Linux-System-Service + - Build + steps: + - name: Get workflow information + id: get-workflow-info + uses: technote-space/workflow-conclusion-action@v3 + + - name: Set Pipeline Exit Status + shell: bash + run: | + if [ "${{ steps.get-workflow-info.outputs.conclusion }}" != "success" ]; then + exit 1 + else + exit 0 + fi + + - name: Done + if: always() + run: + echo "All worflows finished" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9f95d578..2239b2a3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -28,6 +28,19 @@ repos: language: system # <---- Local Hooks ------------------------------------------------------------------------------------------------ + - repo: https://github.com/s0undt3ch/python-tools-scripts + rev: "0.17.0" + hooks: + - id: tools + alias: actionlint + name: Lint GitHub Actions Workflows + files: "^.github/workflows/" + types: + - yaml + args: + - pre-commit + - actionlint + # ----- Formatting ------------------------------------------------------------------------------------------------> - repo: https://github.com/charliermarsh/ruff-pre-commit rev: "v0.0.280" diff --git a/changelog/153.trivial.rst b/changelog/153.trivial.rst new file mode 100644 index 00000000..7b9dfaa6 --- /dev/null +++ b/changelog/153.trivial.rst @@ -0,0 +1 @@ +Start using actionlint and shellcheck to validate GH Actions workflows diff --git a/pyproject.toml b/pyproject.toml index 63a6b7d9..5d83e58c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -212,7 +212,9 @@ ignore = [ "PLR0913", # Too many arguments to function call" "PLR0915", # Too many statements ] - +"tools/**/*.py" = [ + "D104", # Missing docstring in public package +] [tool.ruff.pydocstyle] # Use Google-style docstrings. convention = "google" diff --git a/requirements/tools.txt b/requirements/tools.txt new file mode 100644 index 00000000..80338f31 --- /dev/null +++ b/requirements/tools.txt @@ -0,0 +1 @@ +python-tools-scripts >= 0.17.0 diff --git a/tools/__init__.py b/tools/__init__.py new file mode 100644 index 00000000..01894874 --- /dev/null +++ b/tools/__init__.py @@ -0,0 +1,5 @@ +from __future__ import annotations + +import ptscripts + +ptscripts.register_tools_module("tools.pre_commit") diff --git a/tools/pre_commit.py b/tools/pre_commit.py new file mode 100644 index 00000000..53dd2bdc --- /dev/null +++ b/tools/pre_commit.py @@ -0,0 +1,48 @@ +""" +These commands are used by pre-commit. +""" +from __future__ import annotations + +import logging +import shutil + +from ptscripts import Context +from ptscripts import command_group + +log = logging.getLogger(__name__) + +# Define the command group +cgroup = command_group(name="pre-commit", help="Pre-Commit Related Commands", description=__doc__) + + +@cgroup.command( + name="actionlint", + arguments={ + "files": { + "help": "Files to run actionlint against", + "nargs": "*", + }, + "no_color": { + "help": "Disable colors in output", + }, + }, +) +def actionlint(ctx: Context, files: list[str], no_color: bool = False): + """ + Run `actionlint`. + """ + actionlint = shutil.which("actionlint") + if not actionlint: + ctx.warn("Could not find the 'actionlint' binary") + ctx.exit(0) + cmdline = [actionlint] + if no_color is False: + cmdline.append("-color") + shellcheck = shutil.which("shellcheck") + if shellcheck: + cmdline.append(f"-shellcheck={shellcheck}") + pyflakes = shutil.which("pyflakes") + if pyflakes: + cmdline.append(f"-pyflakes={pyflakes}") + ret = ctx.run(*cmdline, *files, check=False) + ctx.exit(ret.returncode)