From bc66eb24160d663c550a61e99d714a6af47b8c3e Mon Sep 17 00:00:00 2001 From: Rick Peters Date: Sat, 3 Oct 2026 11:54:20 +0200 Subject: [PATCH] ci: Lint workflow (shellcheck, ruff, qmllint) and make lint Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/lint.yml | 60 ++++++++++++++++++++++++++++++++++++++ CHANGELOG.md | 1 + Makefile | 13 +++++++-- ruff.toml | 6 ++++ 4 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/lint.yml create mode 100644 ruff.toml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..bb8f10e --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,60 @@ +# Lint: shell, Python and QML. Separate from bundle.yml (which builds and +# releases) so a style problem shows up as its own check. Runs on GitHub and on +# the Gitea mirror; the same checks locally: make check. +name: Lint + +on: + push: + branches: [main, 'release/**'] + pull_request: + workflow_dispatch: + +permissions: + contents: read + +jobs: + shell: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + # The sources, not only the bundle (bundle.yml checks that): .shellcheckrc + # lists the exceptions (variables shared across modules). + - name: shellcheck the modules + uses: ludeeus/action-shellcheck@2.0.0 + with: + severity: warning + scandir: lib + additional_files: steamify.sh ui/steamify-app.sh .github/tools/bundle.sh + # steamify-os-release.sh is a template (@STEAMOS@ is filled in at install time). + - name: shellcheck the helper scripts + uses: ludeeus/action-shellcheck@2.0.0 + with: + severity: warning + scandir: patches + ignore_names: steamify-os-release.sh + + python: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: ruff (ruff.toml) + uses: astral-sh/ruff-action@v4.1.0 + with: + args: check ui/steamify-ui patches + + qml: + runs-on: ubuntu-latest + # Not blocking until it has run once on the runner image (Qt packages). + continue-on-error: true + steps: + - uses: actions/checkout@v4 + - name: Install qmllint and the QML modules the app imports + run: | + sudo apt-get update -qq + sudo apt-get install -y -qq qt6-declarative-dev-tools qml6-module-qtquick qml6-module-qtquick-controls qml6-module-qtquick-window + # "unqualified" (a screen reading `app` from its parent) is how the screens get their + # AppState, so it is off; everything else must be clean. + - name: qmllint + run: | + lint="$(command -v qmllint6 || echo /usr/lib/qt6/bin/qmllint)" + "$lint" --unqualified disable --max-warnings 0 ui/qml/*.qml diff --git a/CHANGELOG.md b/CHANGELOG.md index 97b9e7b..65e8235 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ one per merged pull request. - **refactor: the entry point picks its mode once (`MODE`) and `--defaults` and `--boot` are functions with one failure report, instead of four booleans and two copies of the apply-and-report code; `NO_COLOR` gives plain output** - **refactor: `is_wanted` and `is_current` instead of repeated `[[ "${WANTED[x]}" == 1 ]]` checks in the menu, the app's backend and the entry point** - **refactor: the start of every item's JSON (id, label, hint, kind, parent) is built in one place (`json_item_head`) for the app and the installer page, and `json_bool` replaces the `&& echo true || echo false` copies** +- **ci: a Lint workflow runs shellcheck on the modules and helper scripts, ruff on the app and `patches/*.py` (`ruff.toml`) and qmllint on the QML (not blocking until it has run once on the runner); `make lint` runs the same locally** - **test: the menu rules, the plan and the app's and installer's JSON are checked against a golden file (`tests/menu-test.sh` in steamify-cachyos-dev); `make check`, `.editorconfig`, `.shellcheckrc`** - **docs: README lists the sources and projects Steamify builds on, with licences and thanks** diff --git a/Makefile b/Makefile index 8059190..0294d90 100644 --- a/Makefile +++ b/Makefile @@ -4,16 +4,25 @@ SH := steamify.sh $(wildcard lib/*.sh) ui/steamify-app.sh .github/tools/bundle.s DEV ?= ../steamify-cachyos-dev TESTS := $(wildcard $(DEV)/tests/*-test.sh) -.PHONY: check syntax lint test bundle fmt-check +.PHONY: check syntax lint lint-sh lint-py lint-qml test bundle fmt-check check: syntax lint test bundle syntax: @for f in $(SH); do bash -n "$$f" || exit 1; done python3 -c 'import ast, sys; [ast.parse(open(f).read(), f) for f in sys.argv[1:]]' ui/steamify-ui patches/*.py -lint: +lint: lint-sh lint-py lint-qml + +lint-sh: @command -v shellcheck >/dev/null && shellcheck -S warning $(SH) || echo "shellcheck not installed: skipped" +lint-py: + @command -v ruff >/dev/null && ruff check ui/steamify-ui patches || echo "ruff not installed: skipped" + +lint-qml: + @Q="$$(command -v qmllint6 || command -v qmllint || ls /usr/lib/qt6/bin/qmllint 2>/dev/null)"; \ + [ -n "$$Q" ] && "$$Q" --unqualified disable --max-warnings 0 ui/qml/*.qml || echo "qmllint not installed: skipped" + test: @for t in $(TESTS); do echo "== $$t"; REPO="$(CURDIR)" bash "$$t" || exit 1; done diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 0000000..2e3c1d1 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,6 @@ +# Python: the app (ui/steamify-ui, no .py extension) and patches/*.py. +line-length = 130 +target-version = "py312" + +[lint] +select = ["E", "F", "W", "B"]