diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 2882b12..0000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,5 +0,0 @@ -github: - - codingjoe - - anapaulagomes - - amureki -custom: https://www.paypal.me/codingjoe diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 7df6849..563cc5c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,10 +1,10 @@ version: 2 updates: -- package-ecosystem: pip - directory: "/" - schedule: - interval: weekly -- package-ecosystem: github-actions - directory: "/" - schedule: - interval: weekly + - package-ecosystem: pip + directory: "/" + schedule: + interval: weekly + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: weekly diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ff178bf..72d4777 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,90 +1,51 @@ name: CI - on: push: branches: - - main + - main pull_request: - jobs: - dist: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - - uses: actions/setup-python@v6 - with: - python-version: "3.x" - - run: python -m pip install --upgrade pip build wheel twine - - run: python -m build --sdist --wheel - - run: python -m twine check dist/* + - uses: astral-sh/setup-uv@v7 + - run: uvx --from build pyproject-build --sdist --wheel + - run: uvx twine check dist/* - uses: actions/upload-artifact@v7 with: path: dist/* - - lint: - runs-on: ubuntu-latest - strategy: - matrix: - lint-command: - - bandit -r . -x ./tests - - black --check --diff . - - flake8 . - - isort --check-only --diff . - - pydocstyle . - steps: - - uses: actions/checkout@v6 - - uses: actions/setup-python@v6 - with: - python-version: "3.x" - cache: 'pip' - cache-dependency-path: 'linter-requirements.txt' - - run: python -m pip install -r linter-requirements.txt - - run: ${{ matrix.lint-command }} - PyTest: runs-on: ubuntu-latest strategy: matrix: python-version: - - "3.8" - - "3.9" - - "3.10" - - "3.11" - + - "3.12" + - "3.13" + - "3.14" steps: - - uses: actions/checkout@v6 - - uses: actions/setup-python@v6 - with: - python-version: ${{ matrix.python-version }} - - run: python -m pip install --upgrade pip setuptools - - run: python -m pip install -e .[test] - - run: relint --version - - run: py.test --cov=. - - uses: codecov/codecov-action@v6 - - + - uses: actions/checkout@v6 + - uses: astral-sh/setup-uv@v7 + with: + python-version: ${{ matrix.python-version }} + - run: uv run relint --version + - run: uv run --group test py.test --cov=. + - uses: codecov/codecov-action@v6 extras: runs-on: ubuntu-latest strategy: matrix: extras: - "regex" - steps: - uses: actions/checkout@v6 - - uses: actions/setup-python@v6 - with: - python-version: "3.x" - - run: python -m pip install --upgrade pip setuptools - - run: python -m pip install -e .[test,${{ matrix.extras }}] - - run: relint --version - - run: py.test --cov=. + - uses: astral-sh/setup-uv@v7 + - run: uv run relint --version + - run: uv run --group test --extra ${{ matrix.extras }} py.test --cov=. - uses: codecov/codecov-action@v6 - analyze: name: CodeQL Analyze - needs: [PyTest,extras] + needs: [PyTest, extras] runs-on: ubuntu-latest permissions: actions: read @@ -93,7 +54,7 @@ jobs: strategy: fail-fast: false matrix: - language: [ python ] + language: [python] steps: - name: Checkout uses: actions/checkout@v6 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f076e2e..350ff01 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,21 +1,28 @@ name: Release - on: release: types: [published] - + workflow_dispatch: jobs: - - PyPi: + pypi-build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 - - uses: actions/setup-python@v6 - with: - python-version: "3.x" - - run: python -m pip install --upgrade pip build wheel twine - - run: python -m build --sdist --wheel - - run: python -m twine upload dist/* - env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} + - uses: actions/checkout@v6 + - uses: astral-sh/setup-uv@v7 + - run: uvx --from build pyproject-build --sdist --wheel + - uses: actions/upload-artifact@v7 + with: + name: release-dists + path: dist/ + pypi-publish: + runs-on: ubuntu-latest + needs: + - pypi-build + permissions: + id-token: write + steps: + - uses: actions/download-artifact@v8 + with: + name: release-dists + path: dist/ + - uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..54da0a3 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,53 @@ +default_language_version: + python: python3.14 +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + - id: trailing-whitespace + - id: check-merge-conflict + - id: check-ast + - id: check-json + - id: pretty-format-json + args: [--autofix] + - id: check-toml + - id: check-xml + - id: check-symlinks + - id: debug-statements + - id: destroyed-symlinks + - id: check-executables-have-shebangs + - id: end-of-file-fixer + - id: name-tests-test + args: ["--pytest-test-first"] + exclude: ^tests/fixtures.py$ + - id: no-commit-to-branch + args: [--branch, main] + - id: detect-private-key + - repo: https://github.com/asottile/pyupgrade + rev: v3.21.2 + hooks: + - id: pyupgrade + args: [--py312-plus] + - repo: https://github.com/hukkin/mdformat + rev: 1.0.0 + hooks: + - id: mdformat + additional_dependencies: + - mdformat-beautysh + - mdformat-footnote + - mdformat-gfm + - mdformat-gfm-alerts + - mdformat-ruff + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.15.14 + hooks: + - id: ruff-check + args: [--fix, --exit-non-zero-on-fix] + - id: ruff-format + - repo: https://github.com/google/yamlfmt + rev: v0.21.0 + hooks: + - id: yamlfmt +ci: + skip: + - no-commit-to-branch diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 89e0d72..f9c6abf 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -1,7 +1,7 @@ -- id: relint - name: relint - description: 'Write your own linting rules using regular expressions.' - entry: relint - args: [--git-diff] - language: python - types: [file] +- id: relint + name: relint + description: 'Write your own linting rules using regular expressions.' + entry: relint + args: [--git-diff] + language: python + types: [file] diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index f9dc270..0000000 --- a/SECURITY.md +++ /dev/null @@ -1,7 +0,0 @@ -# Security Policy - -## Security contact information - -To report a security vulnerability, please use the -[Tidelift security contact](https://tidelift.com/security). -Tidelift will coordinate the fix and disclosure. diff --git a/linter-requirements.txt b/linter-requirements.txt deleted file mode 100644 index b71c749..0000000 --- a/linter-requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -bandit==1.8.6 -black==25.11.0 -flake8==7.3.0 -isort==6.1.0 -pydocstyle[toml]==6.3.0 diff --git a/pyproject.toml b/pyproject.toml index 5dff34e..5f489ad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,21 +23,26 @@ classifiers = [ "Topic :: Software Development :: Quality Assurance", "Topic :: Software Development :: Testing", ] -requires-python = ">=3.8" +requires-python = ">=3.12" dependencies = [ "PyYAML", "rich", ] [project.optional-dependencies] +regex = [ + "regex" +] + +[dependency-groups] +dev = [ + { include-group = "test" }, +] test = [ "pytest", "pytest-cov", "pytest-mock", ] -regex = [ - "regex" -] [project.scripts] relint = "relint.__main__:main" @@ -63,16 +68,29 @@ source = ["relint"] [tool.coverage.report] show_missing = true -[tool.isort] -atomic = true -line_length = 88 -multi_line_output = 3 -force_grid_wrap = 0 -known_first_party = "relint, tests" -include_trailing_comma = true -use_parentheses = true -default_section = "THIRDPARTY" -combine_as_imports = true +[tool.ruff] +src = ["relint", "tests"] + +[tool.ruff.lint] +select = [ + "E", # pycodestyle errors + "W", # pycodestyle warnings + "F", # pyflakes + "I", # isort + "S", # flake8-bandit + "D", # pydocstyle + "UP", # pyupgrade + "B", # flake8-bugbear + "C", # flake8-comprehensions +] + +ignore = ["B904", "D1", "E501", "S101"] + +[tool.ruff.lint.isort] +combine-as-imports = true +split-on-trailing-comma = true +section-order = ["future", "standard-library", "third-party", "first-party", "local-folder"] +force-wrap-aliases = true -[tool.pydocstyle] -add_ignore = "D1" +[tool.ruff.lint.pydocstyle] +convention = "pep257" diff --git a/relint/__main__.py b/relint/__main__.py index ed831d1..21ead42 100644 --- a/relint/__main__.py +++ b/relint/__main__.py @@ -54,11 +54,13 @@ def parse_args(args=None): action="store_true", help="Do not output warnings. Could be useful when using relint in CI.", ) - parser.add_argument( - "--summarize", - action="store_true", - help="Summarize the output by grouping matches by test.", - ), + ( + parser.add_argument( + "--summarize", + action="store_true", + help="Summarize the output by grouping matches by test.", + ), + ) parser.add_argument( "--code-padding", type=int, @@ -89,9 +91,9 @@ def main(args=None): if args.diff: output = sys.stdin.read() elif args.git_diff: - output = subprocess.check_output( # nosec - ["git", "diff", "--staged", "--unified=0", "--no-color"], - universal_newlines=True, + output = subprocess.check_output( + ["git", "diff", "--staged", "--unified=0", "--no-color"], # noqa: S607 + text=True, ) if args.diff or args.git_diff: changed_content = parse_diff(output) diff --git a/relint/config.py b/relint/config.py index 5e9046e..aefc301 100644 --- a/relint/config.py +++ b/relint/config.py @@ -42,7 +42,9 @@ def load_config(path, fail_warnings, ignore_warnings): raise ConfigError("Error parsing your relint config file.") from e except TypeError: warnings.warn( - "Your relint config is empty, no tests were executed.", UserWarning + "Your relint config is empty, no tests were executed.", + UserWarning, + stacklevel=2, ) except (AttributeError, ValueError) as e: raise ConfigError( diff --git a/relint/parse.py b/relint/parse.py index 7aa35a4..c14c9bc 100644 --- a/relint/parse.py +++ b/relint/parse.py @@ -88,7 +88,7 @@ def split_diff_content_by_filename(output: str) -> {str: str}: split_content = GIT_DIFF_SPLIT_PATTERN.split(output) split_content = filter(lambda x: x != "", split_content) - for filename, content in zip(filenames, split_content): + for filename, content in zip(filenames, split_content, strict=True): content_by_filename[filename] = content return content_by_filename @@ -96,7 +96,7 @@ def split_diff_content_by_filename(output: str) -> {str: str}: def print_github_actions_output(matches, args): exit_code = 0 groups = collections.defaultdict(list) - for filename, test, match, line_number in matches: + for filename, test, match, _ in matches: exit_code = test.error if exit_code == 0 else exit_code start_line_no = match.string[: match.start()].count("\n") + 1 end_line_no = match.string[: match.end()].count("\n") + 1 diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 6f60592..0000000 --- a/setup.cfg +++ /dev/null @@ -1,4 +0,0 @@ -[flake8] -max-line-length=88 -select = C,E,F,W,B,B950 -ignore = E203, E501, W503, E731 diff --git a/tests/fixtures/.relint.yml b/tests/fixtures/.relint.yml index d7dec61..f7a6238 100644 --- a/tests/fixtures/.relint.yml +++ b/tests/fixtures/.relint.yml @@ -3,7 +3,6 @@ hint: Get it done right away! filePattern: ^(?!.*test_).*\.(py|js)$ error: false - - name: No fixme (warning) pattern: '[fF][iI][xX][mM][eE]' hint: | @@ -28,10 +27,8 @@ - print('hello world') + console.log('hello world') ``` - filePattern: ^(?!.*test_).*\.(py|js)$ error: true - - name: no hint pattern: '(?i)hint' filePattern: ^(?!.*test_).*\.(py|js)$ diff --git a/tests/test_main.py b/tests/test_main.py index 850aa56..8b1b129 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -2,7 +2,6 @@ import sys import pytest - import relint from relint.__main__ import main @@ -136,9 +135,7 @@ def test_main_execution_with_diff(self, capsys, mocker, tmpdir, fixture_dir): tmpdir.join(".relint.yml").write(config) tmpdir.join("dummy.py").write("# TODO do something") diff = io.StringIO( - "diff --git a/dummy.py b/dummy.py\n" - "@@ -0,0 +1 @@\n" - "+# TODO do something" + "diff --git a/dummy.py b/dummy.py\n@@ -0,0 +1 @@\n+# TODO do something" ) mocker.patch.object(sys, "stdin", diff) diff --git a/tests/test_parse.py b/tests/test_parse.py index 5777d9b..941afbe 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -2,7 +2,6 @@ import warnings import pytest - from relint.__main__ import main from relint.config import Test from relint.exceptions import ConfigError @@ -159,7 +158,7 @@ def test_git_diff(self, capsys, tmpdir, fixture_dir): config = fs.read() tmpdir.join(".relint.yml").write(config) tmpdir.join("dummy.py").write("# TODO do something") - subprocess.check_call(["git", "init"], cwd=tmpdir.strpath) # nosec + subprocess.check_call(["git", "init"], cwd=tmpdir.strpath) # noqa: S607 with tmpdir.as_cwd(): with pytest.raises(SystemExit) as exc_info: