From fcfbf085d04fa77505be36024dd1b324c2938aba Mon Sep 17 00:00:00 2001 From: Dane Hillard Date: Thu, 14 Nov 2024 09:26:36 -0500 Subject: [PATCH] Sort repos of a given type when adding a repo to that type Also: - Add pre-commit config and run against all files --- .github/ISSUE_TEMPLATE/code-issue.md | 5 +- .github/workflows/codeql-analysis.yml | 28 +++++----- .pre-commit-config.yaml | 56 +++++++++++++++++++ LICENSE | 1 - docs/CHANGELOG.md | 1 + ...a-versioning-during-initial-development.md | 2 +- docs/conf.py | 8 ++- pyproject.toml | 7 ++- src/repo_man/commands/add.py | 3 +- test/test_add.py | 30 +++++----- test/test_implode.py | 2 +- test/test_list_repos.py | 12 ++-- test/test_remove.py | 12 ++-- test/test_sniff.py | 12 ++-- test/test_types.py | 8 +-- test/test_utils.py | 2 +- 16 files changed, 126 insertions(+), 63 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.github/ISSUE_TEMPLATE/code-issue.md b/.github/ISSUE_TEMPLATE/code-issue.md index 594c7dd..0635946 100644 --- a/.github/ISSUE_TEMPLATE/code-issue.md +++ b/.github/ISSUE_TEMPLATE/code-issue.md @@ -2,9 +2,8 @@ name: Code issue about: Identify an issue with the source code title: A concise summary of the issue should go here -labels: '' -assignees: '' - +labels: "" +assignees: "" --- [Line(s) of code in question]() diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 6add8d9..5379bc5 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -2,11 +2,11 @@ name: "CodeQL" on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] + branches: [main] schedule: - - cron: '37 17 * * 0' + - cron: "37 17 * * 0" jobs: analyze: @@ -20,19 +20,19 @@ jobs: strategy: fail-fast: false matrix: - language: [ 'python' ] + language: ["python"] steps: - - name: Checkout repository - uses: actions/checkout@v4 + - name: Checkout repository + uses: actions/checkout@v4 - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: ${{ matrix.language }} + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} - - name: Autobuild - uses: github/codeql-action/autobuild@v3 + - name: Autobuild + uses: github/codeql-action/autobuild@v3 - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..1bc47c1 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,56 @@ +#################################################### +# Checks about the pre-commit configuration itself # +#################################################### + +repos: + - repo: meta + hooks: + - id: check-hooks-apply # Ensures all defined hooks affect at least one file in the repo + - id: check-useless-excludes # Ensures all defined excludes apply to at least one file in the repo + + ########################### + # General use / built-ins # + ########################### + + # Click through to this repository to see what other goodies are available + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: check-ast # Checks Python code for syntax errors + - id: trailing-whitespace # Removes trailing whitespace from lines in all file types + - id: end-of-file-fixer # Fixes last line of all file types + - id: check-merge-conflict # Checks if you're about to commit a file that hasn't had conflicts resolved + - id: no-commit-to-branch # Checks if you're committing to a disallowed branch (default is master) + - id: check-toml # Checks TOML files for syntax errors + - id: check-yaml # Checks YAML files for syntax errors + args: [--allow-multiple-documents] + + ########## + # Python # + ########## + + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.7.3 + hooks: + - id: ruff + + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.13.0 + hooks: + - id: mypy + additional_dependencies: [typer, types-termcolor] + + - repo: https://github.com/asottile/pyupgrade + rev: v3.19.0 + hooks: + - id: pyupgrade + args: [--py39-plus] + + ############ + # Markdown # + ############ + + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v3.1.0 + hooks: + - id: prettier diff --git a/LICENSE b/LICENSE index 9ec60d9..1f63e63 100644 --- a/LICENSE +++ b/LICENSE @@ -2,4 +2,3 @@ https://opensource.org/licenses/MIT - diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index a1f2e1d..076ab5f 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Move from `setup.cfg` to `pyproject.toml` for package configuration - Move from `black` to `ruff` for formatting - Move from `isort` to `ruff` for import sorting +- Sort repos of a given type alphabetically when adding a repo to that type ## [0.0.11] - 2024-10-09 diff --git a/docs/architecture/decisions/0002-use-alpha-versioning-during-initial-development.md b/docs/architecture/decisions/0002-use-alpha-versioning-during-initial-development.md index 0f9fe23..4cd430b 100644 --- a/docs/architecture/decisions/0002-use-alpha-versioning-during-initial-development.md +++ b/docs/architecture/decisions/0002-use-alpha-versioning-during-initial-development.md @@ -20,7 +20,7 @@ Use alpha versioning during initial development such that all versions are monot ## Consequences -- Consumers won't know when a change is breaking, and should assume *every* change is breaking +- Consumers won't know when a change is breaking, and should assume _every_ change is breaking - This decision will need to be amended once the project matures into a stable release pattern - There is sometimes confusion about versions like `0.0.996` and people try to install `0.99.6` or similar instead - Every released change during initial development will simply increase the last portion of the version string by one diff --git a/docs/conf.py b/docs/conf.py index 371a223..c1291a0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -13,6 +13,10 @@ import datetime import os from importlib import metadata +from typing import Any + +from sphinx.application import Sphinx + # import sys # sys.path.insert(0, os.path.abspath('.')) @@ -83,7 +87,7 @@ PROJECT_ROOT = Path(__file__).parent.parent PACKAGE_ROOT = PROJECT_ROOT / "src" / "repo_man" - def run_apidoc(_): + def run_apidoc(_: Any) -> None: from sphinx.ext import apidoc apidoc.main([ "--force", @@ -97,5 +101,5 @@ def run_apidoc(_): str(PACKAGE_ROOT / "*.so"), ]) - def setup(app): + def setup(app: Sphinx) -> None: app.connect('builder-inited', run_apidoc) diff --git a/pyproject.toml b/pyproject.toml index f7ff744..de54ebc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -116,13 +116,16 @@ commands = [ ] [tool.tox.env.typecheck] +extras = [ + "docs", +] deps = [ { replace = "ref", of = ["tool", "tox", "env_run_base", "deps"], extend = true }, "mypy", "types-termcolor", ] commands = [ - ["mypy", { replace = "posargs", default = ["src", "test"], extend = true }], + ["mypy", { replace = "posargs", default = ["src", "test", "docs"], extend = true }], ] [tool.tox.env.format] @@ -140,7 +143,7 @@ deps = [ "ruff", ] commands = [ - ["ruff", "check", { replace = "posargs", default = ["src", "test"], extend = true }], + ["ruff", "check", { replace = "posargs", default = ["src", "test", "docs"], extend = true }], ] [tool.tox.env.docs] diff --git a/src/repo_man/commands/add.py b/src/repo_man/commands/add.py index ba053de..eaba41f 100644 --- a/src/repo_man/commands/add.py +++ b/src/repo_man/commands/add.py @@ -30,7 +30,8 @@ def add( config.add_section(repo_type) if "known" not in config[repo_type] or str(repo) not in config[repo_type]["known"].split("\n"): - config.set(repo_type, "known", f"{original_config}\n{repo}") + ordered_repos = sorted(original_config.split("\n") + [str(repo)]) + config.set(repo_type, "known", "\n".join(ordered_repos)) with open(REPO_TYPES_CFG, "w") as config_file: config.write(config_file) diff --git a/test/test_add.py b/test/test_add.py index bc2e26d..66797e0 100644 --- a/test/test_add.py +++ b/test/test_add.py @@ -2,13 +2,13 @@ from pathlib import Path from typing import Callable -import typer +from typer.testing import CliRunner from repo_man.cli import cli def test_add_clean_confirm( - runner: typer.testing.CliRunner, get_config: Callable[[], configparser.ConfigParser] + runner: CliRunner, get_config: Callable[[], configparser.ConfigParser] ) -> None: with runner.isolated_filesystem(): (Path(".") / "some-repo").mkdir() @@ -30,7 +30,7 @@ def test_add_clean_confirm( assert ( config_file.read() == """[some-type] -known = +known = some-repo """ @@ -38,7 +38,7 @@ def test_add_clean_confirm( def test_add_clean_no_confirm_new_file( - runner: typer.testing.CliRunner, get_config: Callable[[], configparser.ConfigParser] + runner: CliRunner, get_config: Callable[[], configparser.ConfigParser] ) -> None: with runner.isolated_filesystem(): (Path(".") / "some-repo").mkdir() @@ -47,14 +47,14 @@ def test_add_clean_no_confirm_new_file( assert result.exit_code == 1 assert ( result.output - == """No repo-man.cfg file found. Do you want to continue? [y/N]: + == """No repo-man.cfg file found. Do you want to continue? [y/N]: Aborted. """ ) def test_add_with_existing_file( - runner: typer.testing.CliRunner, get_config: Callable[[], configparser.ConfigParser] + runner: CliRunner, get_config: Callable[[], configparser.ConfigParser] ) -> None: with runner.isolated_filesystem(): with open("repo-man.cfg", "w") as config_file: @@ -83,11 +83,11 @@ def test_add_with_existing_file( assert ( config_file.read() == """[foo] -known = +known = bar [some-type] -known = +known = some-repo """ @@ -95,7 +95,7 @@ def test_add_with_existing_file( def test_add_with_existing_file_and_type( - runner: typer.testing.CliRunner, get_config: Callable[[], configparser.ConfigParser] + runner: CliRunner, get_config: Callable[[], configparser.ConfigParser] ) -> None: with runner.isolated_filesystem(): with open("repo-man.cfg", "w") as config_file: @@ -116,7 +116,7 @@ def test_add_with_existing_file_and_type( assert ( config_file.read() == """[some-type] -known = +known = bar some-repo @@ -125,7 +125,7 @@ def test_add_with_existing_file_and_type( def test_add_multiple_types( - runner: typer.testing.CliRunner, get_config: Callable[[], configparser.ConfigParser] + runner: CliRunner, get_config: Callable[[], configparser.ConfigParser] ) -> None: with runner.isolated_filesystem(): with open("repo-man.cfg", "w") as config_file: @@ -156,12 +156,12 @@ def test_add_multiple_types( assert ( config_file.read() == """[some-type] -known = +known = bar some-repo [some-other-type] -known = +known = some-repo """ @@ -169,7 +169,7 @@ def test_add_multiple_types( def test_add_no_action_needed( - runner: typer.testing.CliRunner, get_config: Callable[[], configparser.ConfigParser] + runner: CliRunner, get_config: Callable[[], configparser.ConfigParser] ) -> None: with runner.isolated_filesystem(): with open("repo-man.cfg", "w") as config_file: @@ -190,7 +190,7 @@ def test_add_no_action_needed( assert ( config_file.read() == """[some-type] -known = +known = some-repo """ diff --git a/test/test_implode.py b/test/test_implode.py index b115f60..c48ea43 100644 --- a/test/test_implode.py +++ b/test/test_implode.py @@ -30,7 +30,7 @@ def test_implode_when_config_present_no_confirm(runner: typer.testing.CliRunner) assert result.exit_code == 1 assert ( result.output - == """Are you sure you want to do this? [y/N]: + == """Are you sure you want to do this? [y/N]: Aborted. """ ) diff --git a/test/test_list_repos.py b/test/test_list_repos.py index 2a82c59..4a56476 100644 --- a/test/test_list_repos.py +++ b/test/test_list_repos.py @@ -22,7 +22,7 @@ def test_list_repos_with_matches( with open("repo-man.cfg", "w") as config_file: config_file.write( """[foo] -known = +known = some-repo some-other-repo @@ -77,7 +77,7 @@ def test_list_repos_when_long( with open("repo-man.cfg", "w") as config_file: config_file.write( f"""[foo] -known = +known = {config_list} """ @@ -96,11 +96,11 @@ def test_list_repos_for_multiple_tags( with open("repo-man.cfg", "w") as config_file: config_file.write( """[foo] -known = +known = some-repo [bar] -known = +known = some-other-repo """ @@ -124,11 +124,11 @@ def test_list_repos_when_invalid_type( with open("repo-man.cfg", "w") as config_file: config_file.write( """[foo] -known = +known = some-repo [bar] -known = +known = some-other-repo """ diff --git a/test/test_remove.py b/test/test_remove.py index c56c142..05a4c38 100644 --- a/test/test_remove.py +++ b/test/test_remove.py @@ -14,7 +14,7 @@ def test_remove_clean(runner: typer.testing.CliRunner, get_config: Callable[[], with open("repo-man.cfg", "w") as config_file: config_file.write( """[foo] -known = +known = some-repo """ ) @@ -37,7 +37,7 @@ def test_remove_when_invalid_type( with open("repo-man.cfg", "w") as config_file: config_file.write( """[foo] -known = +known = some-repo """ ) @@ -54,7 +54,7 @@ def test_remove_when_invalid_type( assert ( config_file.read() == """[foo] -known = +known = some-repo """ ) @@ -69,11 +69,11 @@ def test_remove_when_unused_type( with open("repo-man.cfg", "w") as config_file: config_file.write( """[foo] -known = +known = some-repo [bar] -known = +known = some-other-repo """ ) @@ -83,7 +83,7 @@ def test_remove_when_unused_type( assert result.exit_code == 1 assert ( result.output - == """Repository 'some-repo' is not configured for type 'bar'. Continue? [y/N]: + == """Repository 'some-repo' is not configured for type 'bar'. Continue? [y/N]: Aborted. """ ) diff --git a/test/test_sniff.py b/test/test_sniff.py index 00eb1ff..3db946e 100644 --- a/test/test_sniff.py +++ b/test/test_sniff.py @@ -12,15 +12,15 @@ def test_known(runner: typer.testing.CliRunner, get_config: Callable[[], configp with open("repo-man.cfg", "w") as config_file: config_file.write( """[foo] -known = +known = some-repo [bar] -known = +known = some-other-repo [ignore] -known = +known = yet-another-repo """ @@ -41,7 +41,7 @@ def test_unconfigured(runner: typer.testing.CliRunner, get_config: Callable[[], with open("repo-man.cfg", "w") as config_file: config_file.write( """[foo] -known = +known = some-repo """ @@ -60,12 +60,12 @@ def test_duplicates(runner: typer.testing.CliRunner, get_config: Callable[[], co with open("repo-man.cfg", "w") as config_file: config_file.write( """[foo] -known = +known = some-repo some-other-repo [bar] -known = +known = some-repo some-other-repo yet-another-repo diff --git a/test/test_types.py b/test/test_types.py index 3732ee8..5999582 100644 --- a/test/test_types.py +++ b/test/test_types.py @@ -25,11 +25,11 @@ def test_types_when_configured( with open("repo-man.cfg", "w") as config_file: config_file.write( """[foo] -known = +known = some-repo [ignore] -known = +known = some-other-repo """ @@ -50,7 +50,7 @@ def test_types_when_not_configured( with open("repo-man.cfg", "w") as config_file: config_file.write( """[foo] -known = +known = some-other-repo """ @@ -71,7 +71,7 @@ def test_types_when_ignored( with open("repo-man.cfg", "w") as config_file: config_file.write( """[ignore] -known = +known = some-repo """ diff --git a/test/test_utils.py b/test/test_utils.py index db6dd6c..70a27a7 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -8,7 +8,7 @@ def test_get_valid_repo_types(runner: typer.testing.CliRunner) -> None: with open("repo-man.cfg", "w") as config_file: config_file.write( """[foo] -known = +known = some-repo """ )