ruff-sync 0.1.5.dev4__tar.gz → 0.1.5.dev5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/ruff-sync-usage/references/ci-integration.md +11 -2
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/PKG-INFO +1 -1
- ruff_sync-0.1.5.dev5/docs/assets/github-job-summary.png +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/docs/ci-integration.md +10 -1
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/docs/usage.md +5 -3
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/pyproject.toml +1 -1
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/src/ruff_sync/formatters.py +111 -20
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/test_formatters.py +140 -31
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/uv.lock +1 -1
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/DEPENDENCIES.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/TESTING.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/decisions/0001-type-refactoring-strategy.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/decisions/0002-tui-node-ast.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/decisions/README.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/formatters-architecture.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/gitlab-reports.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/issue-102-context.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/ruff.toml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/adr/SKILL.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/dirty-equals/SKILL.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/dirty-equals/references/common-matchers.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/dirty-equals/references/toml-matching.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/dirty-equals/trigger_eval.json +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/gh-issues/SKILL.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/mike/SKILL.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/mike/references/commands.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/mkdocs-generation/SKILL.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/mkdocs-generation/examples.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/mkdocs-generation/templates/api-reference.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/mkdocs-generation/templates/getting-started.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/mkdocs-generation/templates/index.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/mkdocs-generation/templates/mkdocs.yml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/release-notes-generation/SKILL.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/ruff-sync-usage/SKILL.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/ruff-sync-usage/references/configuration.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/ruff-sync-usage/references/troubleshooting.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/skill-creator/SKILL.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/skill-creator/references/best-practices.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/skill-creator/references/evaluating-skills.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/skill-creator/references/optimizing-descriptions.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/skill-creator/references/quickstart.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/skill-creator/references/sources.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/skill-creator/references/using-scripts.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/skill-creator/scripts/scaffold_skill.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/textual/SKILL.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/textual/examples/basic_app.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/textual/examples/reactive_example.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/textual/references/events.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/textual/references/styling.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/textual/references/testing.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/textual/references/widgets.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/type-checking/SKILL.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/type-checking/evals.json +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/type-checking/references/advanced-narrowing.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/type-checking/references/error-code-lookup.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/type-checking/references/generics.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/type-checking/references/naming.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/type-checking/references/protocol-patterns.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/type-checking/references/quickstart.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/type-checking/references/refactoring-patterns.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/type-checking/scripts/audit_types.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/tui_design.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/tui_requirements.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/tui_rule_browsing.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/tui_rule_browsing_design.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/workflows/add-test-case.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.git-blame-ignore-revs +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.github/dependabot.yml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.github/workflows/ci.yaml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.github/workflows/complexity.yaml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.github/workflows/docs.yaml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.gitignore +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.pre-commit-config.yaml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.pre-commit-hooks.yaml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/AGENTS.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/CONTRIBUTING.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/LICENSE.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/README.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/codecov.yml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/configs/data-science-engineering/ruff.toml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/configs/fastapi/ruff.toml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/configs/kitchen-sink/ruff.toml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/docs/agent-skill.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/docs/assets/favicon.png +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/docs/assets/github-pr-annotation.png +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/docs/assets/logo.png +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/docs/assets/ruff_sync_banner.png +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/docs/best-practices.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/docs/configuration.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/docs/contributing.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/docs/examples/advanced-config.toml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/docs/examples/basic-config.toml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/docs/gen_ref_pages.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/docs/index.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/docs/installation.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/docs/overrides/main.html +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/docs/overrides/partials/version_warning.html +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/docs/pre-commit.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/docs/pre-defined-configs.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/docs/troubleshooting.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/docs/url-resolution.md +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/mkdocs.yml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/scripts/check_dogfood.sh +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/scripts/gitclone_dogfood.sh +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/scripts/pull_dogfood.sh +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/skills-lock.json +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/src/ruff_sync/__init__.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/src/ruff_sync/__main__.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/src/ruff_sync/cli.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/src/ruff_sync/config_io.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/src/ruff_sync/constants.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/src/ruff_sync/core.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/src/ruff_sync/dependencies.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/src/ruff_sync/pre_commit.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/src/ruff_sync/system.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/src/ruff_sync/tui/__init__.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/src/ruff_sync/tui/app.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/src/ruff_sync/tui/constants.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/src/ruff_sync/tui/screens.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/src/ruff_sync/tui/themes.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/src/ruff_sync/tui/types_.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/src/ruff_sync/tui/widgets.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/src/ruff_sync/types_.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tasks.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/__init__.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/conftest.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/lifecycle_tomls/multi_upstream_final.toml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/lifecycle_tomls/multi_upstream_initial.toml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/lifecycle_tomls/multi_upstream_up1.toml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/lifecycle_tomls/multi_upstream_up2.toml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/lifecycle_tomls/no_changes_final.toml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/lifecycle_tomls/no_changes_initial.toml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/lifecycle_tomls/no_changes_upstream.toml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/lifecycle_tomls/no_dotted_keys_final.toml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/lifecycle_tomls/no_dotted_keys_initial.toml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/lifecycle_tomls/no_dotted_keys_upstream.toml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/lifecycle_tomls/no_ruff_cfg_final.toml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/lifecycle_tomls/no_ruff_cfg_initial.toml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/lifecycle_tomls/no_ruff_cfg_upstream.toml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/lifecycle_tomls/readme_excludes_final.toml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/lifecycle_tomls/readme_excludes_initial.toml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/lifecycle_tomls/readme_excludes_upstream.toml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/lifecycle_tomls/standard_final.toml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/lifecycle_tomls/standard_initial.toml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/lifecycle_tomls/standard_upstream.toml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/ruff.toml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/test_basic.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/test_check.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/test_ci_integration.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/test_ci_validation.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/test_config_io.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/test_config_validation.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/test_constants.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/test_corner_cases.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/test_dependencies.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/test_deprecation.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/test_e2e.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/test_git_fetch.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/test_minimal_imports.sh +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/test_pre_commit.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/test_project.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/test_rule_logic.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/test_scaffold.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/test_serialization.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/test_system.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/test_toml_operations.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/test_url_handling.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/test_whitespace.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/tui/__init__.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/tui/conftest.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/tui/test_themes.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/tui/test_tui.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/tui/test_tui_types.py +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/w_ruff_sync_cfg/pyproject.toml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/wo_ruff_cfg/pyproject.toml +0 -0
- {ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/tests/wo_ruff_sync_cfg/pyproject.toml +0 -0
|
@@ -12,7 +12,10 @@ Add this step to any existing workflow (e.g., `.github/workflows/ci.yaml`):
|
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
`--semantic` ignores cosmetic differences (comments, whitespace) — only real value or rule changes cause failure.
|
|
15
|
-
`--output-format github` creates inline PR annotations
|
|
15
|
+
`--output-format github` creates inline PR annotations and a structured Job Summary report.
|
|
16
|
+
|
|
17
|
+
> [!TIP]
|
|
18
|
+
> `ruff-sync` automatically groups multiple drifts in the same file into a single annotation to reduce PR noise.
|
|
16
19
|
|
|
17
20
|
### Full Workflow Example
|
|
18
21
|
|
|
@@ -73,7 +76,13 @@ For repositories with GitHub Advanced Security enabled, upload SARIF results to
|
|
|
73
76
|
The `|| true` ensures the upload step always runs even when `ruff-sync` exits 1 (drift detected). Without it, GitHub Actions would skip the upload step on failure.
|
|
74
77
|
|
|
75
78
|
> **Why SARIF over `--output-format github`?**
|
|
76
|
-
>
|
|
79
|
+
>
|
|
80
|
+
> | Feature | `github` | `sarif` |
|
|
81
|
+
> |---------|----------|---------|
|
|
82
|
+
> | **PR Feedback** | Inline annotations (grouped) | Inline annotations (per-key) |
|
|
83
|
+
> | **Job Summary** | ✅ Markdown table | ❌ (requires separate parsing) |
|
|
84
|
+
> | **Persistence** | Ephemeral (until re-run) | Persistent (Security tab) |
|
|
85
|
+
> | **Tracking** | Manual | Automated "introduced/resolved" |
|
|
77
86
|
|
|
78
87
|
---
|
|
79
88
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ruff-sync
|
|
3
|
-
Version: 0.1.5.
|
|
3
|
+
Version: 0.1.5.dev5
|
|
4
4
|
Summary: Synchronize Ruff linter configuration across projects
|
|
5
5
|
Project-URL: Homepage, https://github.com/Kilo59/ruff-sync
|
|
6
6
|
Project-URL: Documentation, https://kilo59.github.io/ruff-sync/
|
|
Binary file
|
|
@@ -31,8 +31,17 @@ jobs:
|
|
|
31
31
|
|
|
32
32
|
By using `--output-format github`, `ruff-sync` will emit special workflow commands that GitHub translates into inline annotations directly on your Pull Request's file diff.
|
|
33
33
|
|
|
34
|
+
> [!NOTE]
|
|
35
|
+
> If multiple configuration drifts are detected in the same file, `ruff-sync` automatically groups them into a single, combined annotation to reduce visual noise and preserve your GitHub Actions annotation limits.
|
|
36
|
+
|
|
34
37
|

|
|
35
38
|
|
|
39
|
+
#### Job Summary
|
|
40
|
+
|
|
41
|
+
In addition to inline annotations, `ruff-sync` writes a structured Markdown report to the GitHub Actions **Job Summary** page. This provides a high-level overview of all drifts detected across the entire run, making it easy to see exactly what needs to be synchronized without scrolling through log files.
|
|
42
|
+
|
|
43
|
+

|
|
44
|
+
|
|
36
45
|
### SARIF Upload (GitHub Advanced Security)
|
|
37
46
|
|
|
38
47
|
For repositories with [GitHub Advanced Security](https://docs.github.com/en/get-started/learning-about-github/about-github-advanced-security) enabled, upload SARIF results to track drift findings in the **Security tab** and get per-key inline PR annotations:
|
|
@@ -50,7 +59,7 @@ For repositories with [GitHub Advanced Security](https://docs.github.com/en/get-
|
|
|
50
59
|
|
|
51
60
|
The `|| true` ensures the upload step always runs even when drift is detected (exit code 1).
|
|
52
61
|
|
|
53
|
-
> **Why SARIF over `--output-format github`?** The `github` format
|
|
62
|
+
> **Why SARIF over `--output-format github`?** The `github` format is excellent for lightweight, immediate feedback with a summarized job report. However, SARIF findings are persisted in the Security tab, tracked as "introduced" and "resolved" across branches, and each drifted TOML key (`lint.select`, `target-version`, etc.) appears as a separate, deduplicated finding in the GitHub Security dashboard.
|
|
54
63
|
|
|
55
64
|
### Automated Sync PRs
|
|
56
65
|
|
|
@@ -165,10 +165,12 @@ ruff-sync [UPSTREAM_URL...] [--to PATH] [--exclude KEY...] [--init] [--pre-commi
|
|
|
165
165
|
* **`--exclude KEY...`**: Dotted paths of keys to keep local and never overwrite (e.g., `lint.isort`).
|
|
166
166
|
* **`--init`**: Create a new `pyproject.toml` with the upstream configuration if it doesn't already exist. This automatically saves the upstream source and any other CLI flags into the `[tool.ruff-sync]` section.
|
|
167
167
|
* **`--save` / `--no-save`**: Force serialization (or prevent serialization) of the provided CLI arguments (like `upstream`, `exclude`, etc.) directly into the `[tool.ruff-sync]` section of the target `pyproject.toml` for future use. Note: any credentials present in the upstream URL will cause this operation to safely abort.
|
|
168
|
-
* **`--output-format [text|json|github]`**: Specify the output format for synchronization results.
|
|
168
|
+
* **`--output-format [text|json|github|gitlab|sarif]`**: Specify the output format for synchronization results.
|
|
169
169
|
- `text` (default): Human-readable terminal output.
|
|
170
170
|
- `json`: Machine-readable JSON output for tool integration.
|
|
171
|
-
- `github`: GitHub Actions workflow commands
|
|
171
|
+
- `github`: GitHub Actions workflow commands and Job Summary report.
|
|
172
|
+
- `gitlab`: GitLab Code Quality report (JSON format).
|
|
173
|
+
- `sarif`: Static Analysis Results Interchange Format (SARIF) for GitHub Security or GitLab SAST.
|
|
172
174
|
* **`--pre-commit`**: Sync the `astral-sh/ruff-pre-commit` hook version inside `.pre-commit-config.yaml` with the project's Ruff version.
|
|
173
175
|
|
|
174
176
|
### `check`
|
|
@@ -186,7 +188,7 @@ ruff-sync check [UPSTREAM_URL...] [--semantic] [--diff] [--pre-commit]
|
|
|
186
188
|
* **`UPSTREAM_URL...`**: The source URL(s). Optional if defined locally.
|
|
187
189
|
* **`--semantic`**: Ignore "non-functional" differences like whitespace, comments, or key order. Only errors if the actual Python-level data differs.
|
|
188
190
|
* **`--diff` / `--no-diff`**: Control the display of the unified diff in the terminal.
|
|
189
|
-
* **`--output-format [text|json|github]`**: Specify the output format for check results
|
|
191
|
+
* **`--output-format [text|json|github|gitlab|sarif]`**: Specify the output format for check results (defaults to `text`).
|
|
190
192
|
* **`--pre-commit`**: Verify that the `astral-sh/ruff-pre-commit` hook version matches the project's Ruff version in addition to checking configuration drift. If you have `pre-commit-version-sync = true` configured in your `pyproject.toml`, the `check` command will automatically respect this setting and you do not need to pass this flag.
|
|
191
193
|
|
|
192
194
|
---
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "ruff-sync"
|
|
3
|
-
version = "0.1.5.
|
|
3
|
+
version = "0.1.5.dev5"
|
|
4
4
|
description = "Synchronize Ruff linter configuration across projects"
|
|
5
5
|
keywords = ["ruff", "linter", "config", "synchronize", "python", "linting", "automation", "tomlkit", "pre-commit"]
|
|
6
6
|
authors = [
|
|
@@ -5,9 +5,11 @@ from __future__ import annotations
|
|
|
5
5
|
import hashlib
|
|
6
6
|
import json
|
|
7
7
|
import logging
|
|
8
|
+
import os
|
|
8
9
|
import pathlib
|
|
9
10
|
from typing import Any, Final, Literal, Protocol, TypedDict
|
|
10
11
|
|
|
12
|
+
from ruff_sync.config_io import RuffConfigFileName
|
|
11
13
|
from ruff_sync.constants import OutputFormat
|
|
12
14
|
|
|
13
15
|
LOGGER: Final[logging.Logger] = logging.getLogger(__name__)
|
|
@@ -150,13 +152,29 @@ class TextFormatter:
|
|
|
150
152
|
"""No-op for streaming formatters."""
|
|
151
153
|
|
|
152
154
|
|
|
155
|
+
class GithubIssue(TypedDict):
|
|
156
|
+
"""GitHub Action output issue."""
|
|
157
|
+
|
|
158
|
+
level: Literal["error", "warning"]
|
|
159
|
+
message: str
|
|
160
|
+
file_path: pathlib.Path | None
|
|
161
|
+
drift_key: str | None
|
|
162
|
+
|
|
163
|
+
|
|
153
164
|
class GithubFormatter:
|
|
154
165
|
"""GitHub Actions output formatter.
|
|
155
166
|
|
|
156
167
|
Emits ``::error::`` and ``::warning::`` workflow commands for inline
|
|
157
|
-
annotations
|
|
168
|
+
annotations, and writes a Markdown report to ``GITHUB_STEP_SUMMARY``.
|
|
158
169
|
"""
|
|
159
170
|
|
|
171
|
+
_DEFAULT_PATH: Final[pathlib.Path] = pathlib.Path(RuffConfigFileName.PYPROJECT_TOML)
|
|
172
|
+
|
|
173
|
+
def __init__(self) -> None:
|
|
174
|
+
"""Initialise an empty issue list."""
|
|
175
|
+
self._errors: list[GithubIssue] = []
|
|
176
|
+
self._warnings: list[GithubIssue] = []
|
|
177
|
+
|
|
160
178
|
@staticmethod
|
|
161
179
|
def _escape(value: str, is_property: bool = False) -> str:
|
|
162
180
|
r"""Escapes characters for GitHub Actions workflow commands.
|
|
@@ -170,6 +188,11 @@ class GithubFormatter:
|
|
|
170
188
|
return escaped.replace(":", "%3A").replace(",", "%2C")
|
|
171
189
|
return escaped
|
|
172
190
|
|
|
191
|
+
@staticmethod
|
|
192
|
+
def _strip_status_prefix(message: str) -> str:
|
|
193
|
+
"""Return the message without leading status emoji (e.g. ❌, ⚠️)."""
|
|
194
|
+
return message.removeprefix("❌ ").removeprefix("⚠️ ")
|
|
195
|
+
|
|
173
196
|
def note(self, message: str) -> None:
|
|
174
197
|
"""Print a status note (standard stdout)."""
|
|
175
198
|
print(message)
|
|
@@ -190,17 +213,17 @@ class GithubFormatter:
|
|
|
190
213
|
check_name: str = _DEFAULT_CHECK_NAME,
|
|
191
214
|
drift_key: str | None = None,
|
|
192
215
|
) -> None:
|
|
193
|
-
"""
|
|
216
|
+
"""Accumulate an error-level finding."""
|
|
194
217
|
# Delegate standard log output to the logger to preserve context
|
|
195
218
|
(logger or LOGGER).error(message)
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
219
|
+
self._errors.append(
|
|
220
|
+
{
|
|
221
|
+
"level": "error",
|
|
222
|
+
"message": message,
|
|
223
|
+
"file_path": file_path,
|
|
224
|
+
"drift_key": drift_key,
|
|
225
|
+
}
|
|
226
|
+
)
|
|
204
227
|
|
|
205
228
|
def warning(
|
|
206
229
|
self,
|
|
@@ -210,16 +233,16 @@ class GithubFormatter:
|
|
|
210
233
|
check_name: str = _DEFAULT_CHECK_NAME,
|
|
211
234
|
drift_key: str | None = None,
|
|
212
235
|
) -> None:
|
|
213
|
-
"""
|
|
236
|
+
"""Accumulate a warning-level finding."""
|
|
214
237
|
(logger or LOGGER).warning(message)
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
238
|
+
self._warnings.append(
|
|
239
|
+
{
|
|
240
|
+
"level": "warning",
|
|
241
|
+
"message": message,
|
|
242
|
+
"file_path": file_path,
|
|
243
|
+
"drift_key": drift_key,
|
|
244
|
+
}
|
|
245
|
+
)
|
|
223
246
|
|
|
224
247
|
def debug(self, message: str, logger: logging.Logger | None = None) -> None:
|
|
225
248
|
"""Print a debug message as a GitHub Action debug annotation."""
|
|
@@ -232,7 +255,75 @@ class GithubFormatter:
|
|
|
232
255
|
print(diff_text, end="")
|
|
233
256
|
|
|
234
257
|
def finalize(self) -> None:
|
|
235
|
-
"""
|
|
258
|
+
"""Finalize and emit all accumulated issues as GitHub workflow commands.
|
|
259
|
+
|
|
260
|
+
If multiple issues exist for a single file/level, they are combined into
|
|
261
|
+
a single multi-line annotation to reduce visual noise.
|
|
262
|
+
|
|
263
|
+
A Markdown summary is also written to ``GITHUB_STEP_SUMMARY`` if the
|
|
264
|
+
environment variable is set.
|
|
265
|
+
"""
|
|
266
|
+
all_issues = self._errors + self._warnings
|
|
267
|
+
|
|
268
|
+
# 1. Emit Inline Annotations
|
|
269
|
+
self._emit_annotations()
|
|
270
|
+
|
|
271
|
+
# 2. Write Step Summary
|
|
272
|
+
self._write_summary(all_issues)
|
|
273
|
+
|
|
274
|
+
def _emit_annotations(self) -> None:
|
|
275
|
+
"""Group and emit issues as GitHub Action workflow commands."""
|
|
276
|
+
for level, issues in [("error", self._errors), ("warning", self._warnings)]:
|
|
277
|
+
# Group by file path object
|
|
278
|
+
by_file: dict[pathlib.Path, list[GithubIssue]] = {}
|
|
279
|
+
for issue in issues:
|
|
280
|
+
file_key = issue["file_path"] or self._DEFAULT_PATH
|
|
281
|
+
by_file.setdefault(file_key, []).append(issue)
|
|
282
|
+
|
|
283
|
+
for file_path, file_issues in by_file.items():
|
|
284
|
+
if not file_issues:
|
|
285
|
+
continue
|
|
286
|
+
|
|
287
|
+
title_prefix = "Error" if level == "error" else "Warning"
|
|
288
|
+
title_val = self._escape(f"Ruff Sync {title_prefix}", is_property=True)
|
|
289
|
+
file_val = self._escape(str(file_path), is_property=True)
|
|
290
|
+
file_arg = f"file={file_val},line=1,"
|
|
291
|
+
|
|
292
|
+
if len(file_issues) == 1:
|
|
293
|
+
issue = file_issues[0]
|
|
294
|
+
clean_msg = self._strip_status_prefix(issue["message"])
|
|
295
|
+
escaped_msg = self._escape(clean_msg)
|
|
296
|
+
else:
|
|
297
|
+
# Combine into a single bulleted annotation
|
|
298
|
+
combined_msg = f"Multiple ruff-sync {level}s in {file_path}:"
|
|
299
|
+
for issue in file_issues:
|
|
300
|
+
clean_line = self._strip_status_prefix(issue["message"])
|
|
301
|
+
combined_msg += f"\n- {clean_line}"
|
|
302
|
+
escaped_msg = self._escape(combined_msg)
|
|
303
|
+
|
|
304
|
+
print(f"::{level} {file_arg}title={title_val}::{escaped_msg}")
|
|
305
|
+
|
|
306
|
+
def _write_summary(self, all_issues: list[GithubIssue]) -> None:
|
|
307
|
+
"""Write a Markdown report to the GitHub Step Summary file."""
|
|
308
|
+
summary_file = os.environ.get("GITHUB_STEP_SUMMARY")
|
|
309
|
+
if not summary_file or not all_issues:
|
|
310
|
+
return
|
|
311
|
+
|
|
312
|
+
summary_path = pathlib.Path(summary_file)
|
|
313
|
+
try:
|
|
314
|
+
with summary_path.open("a", encoding="utf-8") as f:
|
|
315
|
+
f.write("### 🔄 Ruff-Sync Drift Report\n\n")
|
|
316
|
+
f.write("| Severity | Key | Message | File |\n")
|
|
317
|
+
f.write("| :--- | :--- | :--- | :--- |\n")
|
|
318
|
+
for issue in all_issues:
|
|
319
|
+
severity = "❌ Error" if issue["level"] == "error" else "⚠️ Warning"
|
|
320
|
+
key = issue["drift_key"] or "-"
|
|
321
|
+
clean_msg = self._strip_status_prefix(issue["message"])
|
|
322
|
+
file_path = issue["file_path"] or self._DEFAULT_PATH
|
|
323
|
+
f.write(f"| {severity} | `{key}` | {clean_msg} | `{file_path.name}` |\n")
|
|
324
|
+
f.write("\n")
|
|
325
|
+
except OSError:
|
|
326
|
+
LOGGER.exception(f"Failed to write to GITHUB_STEP_SUMMARY: {summary_file}")
|
|
236
327
|
|
|
237
328
|
|
|
238
329
|
class JsonFormatter:
|
|
@@ -26,9 +26,11 @@ if TYPE_CHECKING:
|
|
|
26
26
|
# ---------------------------------------------------------------------------
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
@pytest.fixture(
|
|
29
|
+
@pytest.fixture(
|
|
30
|
+
params=[TextFormatter, GithubFormatter, JsonFormatter, GitlabFormatter, SarifFormatter]
|
|
31
|
+
)
|
|
30
32
|
def formatter(request: pytest.FixtureRequest) -> ResultFormatter:
|
|
31
|
-
"""Fixture providing instances of
|
|
33
|
+
"""Fixture providing instances of ALL available formatters."""
|
|
32
34
|
formatter_cls: type[ResultFormatter] = request.param
|
|
33
35
|
return formatter_cls()
|
|
34
36
|
|
|
@@ -53,26 +55,39 @@ class TestFormatterBasics:
|
|
|
53
55
|
) -> None:
|
|
54
56
|
"""Verify methods that primarily output to stdout."""
|
|
55
57
|
getattr(formatter, method)(message)
|
|
58
|
+
formatter.finalize()
|
|
56
59
|
captured = capsys.readouterr().out
|
|
57
60
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
61
|
+
match formatter:
|
|
62
|
+
case JsonFormatter():
|
|
63
|
+
# diff strip()s input in implementation
|
|
64
|
+
expected_msg = message.strip() if method == "diff" else message
|
|
65
|
+
assert json.loads(captured) == {"level": method, "message": expected_msg}
|
|
66
|
+
case GitlabFormatter() | SarifFormatter():
|
|
67
|
+
# These only output on finalize, and don't output notes/success/diffs
|
|
68
|
+
# (except Gitlab potentially logging them via logger)
|
|
69
|
+
pass
|
|
70
|
+
case _:
|
|
71
|
+
# Text and Github print raw for these (Github prints notes/success immediately)
|
|
72
|
+
assert message in captured
|
|
73
|
+
|
|
74
|
+
def test_finalize_is_safe_and_reproducible(
|
|
67
75
|
self,
|
|
68
76
|
formatter: ResultFormatter,
|
|
69
77
|
capsys: pytest.CaptureFixture[str],
|
|
70
78
|
) -> None:
|
|
71
|
-
"""finalize() must
|
|
79
|
+
"""finalize() must be safe to call and only produce output for accumulating types."""
|
|
72
80
|
formatter.finalize()
|
|
73
81
|
captured = capsys.readouterr()
|
|
74
|
-
|
|
75
|
-
|
|
82
|
+
|
|
83
|
+
match formatter:
|
|
84
|
+
case GithubFormatter() | GitlabFormatter() | SarifFormatter():
|
|
85
|
+
# Accumulating formatters might output empty lists or valid JSON/Markdown
|
|
86
|
+
pass
|
|
87
|
+
case _:
|
|
88
|
+
# Strictly streaming formatters should have no output on empty finalize()
|
|
89
|
+
assert captured.out == ""
|
|
90
|
+
assert captured.err == ""
|
|
76
91
|
|
|
77
92
|
@pytest.mark.parametrize(
|
|
78
93
|
"method, level",
|
|
@@ -97,45 +112,139 @@ class TestFormatterBasics:
|
|
|
97
112
|
with caplog.at_level(level, logger="ruff_sync.formatters"):
|
|
98
113
|
getattr(formatter, method)(message)
|
|
99
114
|
|
|
115
|
+
formatter.finalize()
|
|
100
116
|
captured = capsys.readouterr().out
|
|
101
117
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
118
|
+
match formatter:
|
|
119
|
+
case TextFormatter():
|
|
120
|
+
# TextFormatter only logs, never prints diagnostics
|
|
121
|
+
assert captured == ""
|
|
122
|
+
assert message in caplog.text
|
|
123
|
+
case GithubFormatter():
|
|
124
|
+
# GithubFormatter logs AND prints workflow commands
|
|
125
|
+
assert message in caplog.text
|
|
126
|
+
if method != "info":
|
|
127
|
+
assert f"::{method}" in captured
|
|
128
|
+
case JsonFormatter():
|
|
129
|
+
# JsonFormatter only prints JSON object per diagnostic
|
|
130
|
+
assert message in captured
|
|
131
|
+
case GitlabFormatter():
|
|
132
|
+
# Gitlab only accumulates error/warning
|
|
133
|
+
if method in ("error", "warning"):
|
|
134
|
+
data = json.loads(captured)
|
|
135
|
+
assert any(issue["description"] == message for issue in data)
|
|
136
|
+
case SarifFormatter():
|
|
137
|
+
# Sarif only accumulates error/warning
|
|
138
|
+
if method in ("error", "warning"):
|
|
139
|
+
data = json.loads(captured)
|
|
140
|
+
assert any(r["message"]["text"] == message for r in data["runs"][0]["results"])
|
|
116
141
|
|
|
117
142
|
|
|
118
143
|
class TestGithubFormatterSpecifics:
|
|
144
|
+
"""Tests for GithubFormatter (GitHub Actions workflow commands)."""
|
|
145
|
+
|
|
119
146
|
def test_escape(self) -> None:
|
|
120
147
|
"""Verify GitHub-specific character escaping."""
|
|
121
148
|
assert GithubFormatter._escape("a%b\rc\nd") == "a%25b%0Dc%0Ad"
|
|
122
149
|
assert GithubFormatter._escape("a:b,c", is_property=True) == "a%3Ab%2Cc"
|
|
123
150
|
|
|
151
|
+
def test_note_and_success_are_streaming(self, capsys: pytest.CaptureFixture[str]) -> None:
|
|
152
|
+
"""note() and success() should emit output immediately."""
|
|
153
|
+
fmt = GithubFormatter()
|
|
154
|
+
fmt.note("note msg")
|
|
155
|
+
fmt.success("success msg")
|
|
156
|
+
captured = capsys.readouterr().out
|
|
157
|
+
assert "note msg" in captured
|
|
158
|
+
assert "success msg" in captured
|
|
159
|
+
|
|
160
|
+
def test_debug_is_streaming(self, capsys: pytest.CaptureFixture[str]) -> None:
|
|
161
|
+
"""debug() should emit ::debug:: immediately."""
|
|
162
|
+
fmt = GithubFormatter()
|
|
163
|
+
fmt.debug("debug msg")
|
|
164
|
+
captured = capsys.readouterr().out
|
|
165
|
+
assert "::debug::debug msg" in captured
|
|
166
|
+
|
|
167
|
+
def test_error_and_warning_are_accumulated(self, capsys: pytest.CaptureFixture[str]) -> None:
|
|
168
|
+
"""error() and warning() must not emit output until finalize() is called."""
|
|
169
|
+
fmt = GithubFormatter()
|
|
170
|
+
fmt.error("error msg")
|
|
171
|
+
fmt.warning("warning msg")
|
|
172
|
+
|
|
173
|
+
captured = capsys.readouterr().out
|
|
174
|
+
assert "::error" not in captured
|
|
175
|
+
assert "::warning" not in captured
|
|
176
|
+
|
|
177
|
+
fmt.finalize()
|
|
178
|
+
captured = capsys.readouterr().out
|
|
179
|
+
assert "::error" in captured
|
|
180
|
+
assert "::warning" in captured
|
|
181
|
+
|
|
124
182
|
def test_emoji_stripping(self, capsys: pytest.CaptureFixture[str]) -> None:
|
|
125
183
|
"""Ensure leading status icons are stripped from GitHub-specific output."""
|
|
126
184
|
fmt = GithubFormatter()
|
|
127
185
|
fmt.error("❌ error msg")
|
|
128
186
|
fmt.warning("⚠️ warning msg")
|
|
187
|
+
fmt.finalize()
|
|
129
188
|
|
|
130
189
|
captured = capsys.readouterr().out
|
|
131
190
|
assert "title=Ruff Sync Error::error msg" in captured
|
|
132
191
|
assert "title=Ruff Sync Warning::warning msg" in captured
|
|
133
192
|
|
|
134
|
-
def
|
|
135
|
-
"""
|
|
193
|
+
def test_single_issue_has_precision(self, capsys: pytest.CaptureFixture[str]) -> None:
|
|
194
|
+
"""A single issue should still include line=1 and the raw message."""
|
|
136
195
|
fmt = GithubFormatter()
|
|
137
196
|
fmt.error("msg", file_path=pathlib.Path("src/foo.py"))
|
|
138
|
-
|
|
197
|
+
fmt.finalize()
|
|
198
|
+
captured = capsys.readouterr().out
|
|
199
|
+
assert "file=src/foo.py,line=1," in captured
|
|
200
|
+
assert "::error" in captured
|
|
201
|
+
assert "::msg" in captured
|
|
202
|
+
|
|
203
|
+
def test_multiple_issues_are_combined(self, capsys: pytest.CaptureFixture[str]) -> None:
|
|
204
|
+
"""Multiple issues in the same file should be combined into one annotation."""
|
|
205
|
+
fmt = GithubFormatter()
|
|
206
|
+
path = pathlib.Path("pyproject.toml")
|
|
207
|
+
fmt.error("msg 1", file_path=path)
|
|
208
|
+
fmt.error("msg 2", file_path=path)
|
|
209
|
+
fmt.finalize()
|
|
210
|
+
|
|
211
|
+
captured = capsys.readouterr().out
|
|
212
|
+
# Should only have ONE ::error line
|
|
213
|
+
assert captured.count("::error") == 1
|
|
214
|
+
assert "Multiple ruff-sync errors in pyproject.toml" in captured
|
|
215
|
+
assert "msg 1" in captured
|
|
216
|
+
assert "msg 2" in captured
|
|
217
|
+
|
|
218
|
+
def test_step_summary_logic(
|
|
219
|
+
self,
|
|
220
|
+
tmp_path: pathlib.Path,
|
|
221
|
+
capsys: pytest.CaptureFixture[str],
|
|
222
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
223
|
+
) -> None:
|
|
224
|
+
"""Verify the Markdown summary is written to GITHUB_STEP_SUMMARY."""
|
|
225
|
+
summary_file = tmp_path / "summary.md"
|
|
226
|
+
monkeypatch.setenv("GITHUB_STEP_SUMMARY", str(summary_file))
|
|
227
|
+
|
|
228
|
+
fmt = GithubFormatter()
|
|
229
|
+
fmt.error("error msg", drift_key="lint.select")
|
|
230
|
+
fmt.warning("warn msg", drift_key="target-version")
|
|
231
|
+
fmt.finalize()
|
|
232
|
+
|
|
233
|
+
content = summary_file.read_text()
|
|
234
|
+
assert "### 🔄 Ruff-Sync Drift Report" in content
|
|
235
|
+
assert "❌ Error" in content
|
|
236
|
+
assert "⚠️ Warning" in content
|
|
237
|
+
assert "| `lint.select` | error msg |" in content
|
|
238
|
+
assert "| `target-version` | warn msg |" in content
|
|
239
|
+
|
|
240
|
+
def test_step_summary_no_env(
|
|
241
|
+
self, tmp_path: pathlib.Path, capsys: pytest.CaptureFixture[str]
|
|
242
|
+
) -> None:
|
|
243
|
+
"""Finalize should work fine and output nothing to summary if env var is missing."""
|
|
244
|
+
fmt = GithubFormatter()
|
|
245
|
+
fmt.error("error")
|
|
246
|
+
fmt.finalize()
|
|
247
|
+
# No crash occurs
|
|
139
248
|
|
|
140
249
|
|
|
141
250
|
class TestJsonFormatterSpecifics:
|
|
File without changes
|
|
File without changes
|
{ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/decisions/0001-type-refactoring-strategy.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/mkdocs-generation/templates/index.md
RENAMED
|
File without changes
|
{ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/mkdocs-generation/templates/mkdocs.yml
RENAMED
|
File without changes
|
{ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/release-notes-generation/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/skill-creator/references/quickstart.md
RENAMED
|
File without changes
|
{ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/skill-creator/references/sources.md
RENAMED
|
File without changes
|
|
File without changes
|
{ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/skill-creator/scripts/scaffold_skill.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ruff_sync-0.1.5.dev4 → ruff_sync-0.1.5.dev5}/.agents/skills/textual/examples/reactive_example.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|