Skip to content

Commit 848b9dc

Browse files
Modernize Codebase and Enhance CI Workflow (#67)
Updated Type Hints: Used pyupgrade with --py39-plus and --py310-plus flags to upgrade type hints to Python 3.9+ and 3.10+ syntax. Enforced Double Quotes: Removed skip-string-normalization = true from the black configuration in pyproject.toml. Reran black via pre-commit hooks to enforce double quotes in the codebase. Refactored Dependency Management: Split requirements.txt into requirements.txt (runtime dependencies) and requirements-dev.txt (development dependencies). Enhanced CI Workflow: Integrated pre-commit hooks into the CI pipeline to enforce code quality checks automatically. Added pip caching to the CI workflow to speed up dependency installation. Automated Package Publishing: Added a publish.yml GitHub Actions workflow to automate publishing to PyPI. The workflow triggers on release creation or manual dispatch, builds the package, and publishes it to PyPI using twine.
1 parent 3c5e7e9 commit 848b9dc

21 files changed

+398
-342
lines changed

.github/workflows/ci.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
test:
11+
runs-on: ${{ matrix.os }}
12+
strategy:
13+
fail-fast: true
14+
matrix:
15+
os: [ubuntu-latest, windows-latest, macos-latest]
16+
python-version: ["3.10", "3.11", "3.12", "3.13"]
17+
18+
steps:
19+
- uses: actions/checkout@v4
20+
21+
- name: Set up Python
22+
uses: actions/setup-python@v5
23+
with:
24+
python-version: ${{ matrix.python-version }}
25+
26+
- name: Cache pip
27+
uses: actions/cache@v4
28+
with:
29+
path: ~/.cache/pip
30+
key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements*.txt') }}
31+
restore-keys: |
32+
${{ runner.os }}-pip-
33+
34+
- name: Install dependencies
35+
run: |
36+
pip install --upgrade pip
37+
pip install -r requirements-dev.txt
38+
39+
- name: Run tests
40+
run: |
41+
pytest
42+
43+
# Run pre-commit only on Python 3.13 + ubuntu.
44+
- name: Run pre-commit hooks
45+
if: ${{ matrix.python-version == '3.13' && matrix.os == 'ubuntu-latest' }}
46+
run: |
47+
pre-commit run --all-files

.github/workflows/publish.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: Publish to PyPI
2+
3+
on:
4+
release:
5+
types: [created] # Trigger only when a release is created
6+
workflow_dispatch: # Allows manual triggering of the workflow
7+
8+
jobs:
9+
publish:
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
# Step 1: Check out the code
14+
- name: Checkout code
15+
uses: actions/checkout@v4
16+
17+
# Step 2: Set up Python
18+
- name: Set up Python
19+
uses: actions/setup-python@v5
20+
with:
21+
python-version: 3.13
22+
23+
# Step 3: Install dependencies for building and publishing
24+
- name: Install build tools
25+
run: |
26+
pip install --upgrade pip
27+
pip install build twine
28+
29+
# Step 4: Build the package
30+
- name: Build the package
31+
run: |
32+
python -m build
33+
34+
# Step 5: Publish to PyPI
35+
- name: Publish to PyPI
36+
env:
37+
TWINE_USERNAME: __token__
38+
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
39+
run: |
40+
python -m twine check dist/*
41+
python -m twine upload --skip-existing dist/*

.github/workflows/unitest.yml

Lines changed: 0 additions & 33 deletions
This file was deleted.

.pre-commit-config.yaml

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,39 @@ repos:
44
hooks:
55
# Files
66
- id: check-added-large-files
7-
description: 'Prevent large files from being committed.'
8-
args: ['--maxkb=10000']
7+
description: "Prevent large files from being committed."
8+
args: ["--maxkb=10000"]
99
- id: check-case-conflict
10-
description: 'Check for files that would conflict in case-insensitive filesystems.'
10+
description: "Check for files that would conflict in case-insensitive filesystems."
1111
- id: fix-byte-order-marker
12-
description: 'Remove utf-8 byte order marker.'
12+
description: "Remove utf-8 byte order marker."
1313
- id: mixed-line-ending
14-
description: 'Replace mixed line ending.'
14+
description: "Replace mixed line ending."
1515

1616
# Links
1717
- id: destroyed-symlinks
18-
description: 'Detect symlinks which are changed to regular files with a content of a path which that symlink was pointing to.'
18+
description: "Detect symlinks which are changed to regular files with a content of a path which that symlink was pointing to."
1919

2020
# File files for parseable syntax: python
2121
- id: check-ast
2222

2323
# File and line endings
2424
- id: end-of-file-fixer
25-
description: 'Ensure that a file is either empty, or ends with one newline.'
25+
description: "Ensure that a file is either empty, or ends with one newline."
2626
- id: trailing-whitespace
27-
description: 'Trim trailing whitespace.'
27+
description: "Trim trailing whitespace."
2828

2929
# Python
3030
- id: check-docstring-first
31-
description: 'Check a common error of defining a docstring after code.'
31+
description: "Check a common error of defining a docstring after code."
3232
- id: requirements-txt-fixer
33-
description: 'Sort entries in requirements.txt.'
33+
description: "Sort entries in requirements.txt."
3434

3535
- repo: https://github.com/MarcoGorelli/absolufy-imports
3636
rev: v0.3.1
3737
hooks:
3838
- id: absolufy-imports
39-
description: 'Automatically convert relative imports to absolute. (Use `args: [--never]` to revert.)'
39+
description: "Automatically convert relative imports to absolute. (Use `args: [--never]` to revert.)"
4040

4141
- repo: https://github.com/psf/black
4242
rev: 24.10.0
@@ -47,30 +47,30 @@ repos:
4747
rev: v3.19.1
4848
hooks:
4949
- id: pyupgrade
50-
description: 'Automatically upgrade syntax for newer versions.'
51-
args: [--py3-plus, --py36-plus, --py38-plus]
50+
description: "Automatically upgrade syntax for newer versions."
51+
args: [--py3-plus, --py36-plus, --py38-plus, --py39-plus, --py310-plus]
5252

5353
- repo: https://github.com/pre-commit/pygrep-hooks
5454
rev: v1.10.0
5555
hooks:
5656
- id: python-check-blanket-noqa
57-
description: 'Enforce that `noqa` annotations always occur with specific codes. Sample annotations: `# noqa: F401`, `# noqa: F401,W203`.'
57+
description: "Enforce that `noqa` annotations always occur with specific codes. Sample annotations: `# noqa: F401`, `# noqa: F401,W203`."
5858
- id: python-check-blanket-type-ignore
59-
description: 'Enforce that `# type: ignore` annotations always occur with specific codes. Sample annotations: `# type: ignore[attr-defined]`, `# type: ignore[attr-defined, name-defined]`.'
59+
description: "Enforce that `# type: ignore` annotations always occur with specific codes. Sample annotations: `# type: ignore[attr-defined]`, `# type: ignore[attr-defined, name-defined]`."
6060
- id: python-use-type-annotations
61-
description: 'Enforce that python3.6+ type annotations are used instead of type comments.'
61+
description: "Enforce that python3.6+ type annotations are used instead of type comments."
6262

6363
- repo: https://github.com/PyCQA/isort
6464
rev: 5.13.2
6565
hooks:
6666
- id: isort
67-
description: 'Sort imports alphabetically, and automatically separated into sections and by type.'
67+
description: "Sort imports alphabetically, and automatically separated into sections and by type."
6868

6969
- repo: https://github.com/hadialqattan/pycln
7070
rev: v2.4.0
7171
hooks:
7272
- id: pycln
73-
description: 'Remove unused import statements.'
73+
description: "Remove unused import statements."
7474

7575
- repo: https://github.com/djlint/djLint
7676
rev: v1.36.4

pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,3 @@ filter_files = true
1414

1515
[tool.black]
1616
line-length = 119
17-
skip-string-normalization = true

requirements-dev.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
-r requirements.txt
2+
black
3+
djlint
4+
pre-commit
5+
pylint
6+
pytest
7+
pytest-asyncio

requirements.txt

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
1-
black
21
click>=8.0.0
3-
djlint
42
fastapi-analytics
53
fastapi[standard]
6-
pre-commit
7-
pytest
8-
pytest-asyncio
94
python-dotenv
105
slowapi
116
starlette

src/gitingest/cli.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import os
2-
from typing import Optional, Tuple
32

43
import click
54

6-
from gitingest.ignore_patterns import DEFAULT_IGNORE_PATTERNS
75
from gitingest.ingest import ingest
86
from gitingest.ingest_from_query import MAX_FILE_SIZE
97

@@ -17,17 +15,17 @@ def normalize_pattern(pattern: str) -> str:
1715

1816

1917
@click.command()
20-
@click.argument('source', type=str, required=True)
21-
@click.option('--output', '-o', default=None, help='Output file path (default: <repo_name>.txt in current directory)')
22-
@click.option('--max-size', '-s', default=MAX_FILE_SIZE, help='Maximum file size to process in bytes')
23-
@click.option('--exclude-pattern', '-e', multiple=True, help='Patterns to exclude')
24-
@click.option('--include-pattern', '-i', multiple=True, help='Patterns to include')
18+
@click.argument("source", type=str, required=True)
19+
@click.option("--output", "-o", default=None, help="Output file path (default: <repo_name>.txt in current directory)")
20+
@click.option("--max-size", "-s", default=MAX_FILE_SIZE, help="Maximum file size to process in bytes")
21+
@click.option("--exclude-pattern", "-e", multiple=True, help="Patterns to exclude")
22+
@click.option("--include-pattern", "-i", multiple=True, help="Patterns to include")
2523
def main(
2624
source: str,
27-
output: Optional[str],
25+
output: str | None,
2826
max_size: int,
29-
exclude_pattern: Tuple[str, ...],
30-
include_pattern: Tuple[str, ...],
27+
exclude_pattern: tuple[str, ...],
28+
include_pattern: tuple[str, ...],
3129
) -> None:
3230
"""Analyze a directory and create a text dump of its contents."""
3331
try:
@@ -48,5 +46,5 @@ def main(
4846
raise click.Abort()
4947

5048

51-
if __name__ == '__main__':
49+
if __name__ == "__main__":
5250
main()

src/gitingest/clone.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import asyncio
22
from dataclasses import dataclass
3-
from typing import Optional, Tuple
43

54
from gitingest.utils import AsyncTimeoutError, async_timeout
65

@@ -11,8 +10,9 @@
1110
class CloneConfig:
1211
url: str
1312
local_path: str
14-
commit: Optional[str] = None
15-
branch: Optional[str] = None
13+
commit: str | None = None
14+
branch: str | None = None
15+
1616

1717

1818
async def check_repo_exists(url: str) -> bool:
@@ -44,7 +44,8 @@ async def check_repo_exists(url: str) -> bool:
4444
return "HTTP/1.1 404" not in stdout_str and "HTTP/2 404" not in stdout_str
4545

4646

47-
async def run_git_command(*args: str) -> Tuple[bytes, bytes]:
47+
async def run_git_command(*args: str) -> tuple[bytes, bytes]:
48+
4849
"""
4950
Executes a git command asynchronously and captures its output.
5051
@@ -77,7 +78,7 @@ async def run_git_command(*args: str) -> Tuple[bytes, bytes]:
7778

7879

7980
@async_timeout(CLONE_TIMEOUT)
80-
async def clone_repo(config: CloneConfig) -> Tuple[bytes, bytes]:
81+
async def clone_repo(config: CloneConfig) -> tuple[bytes, bytes]:
8182
"""
8283
Clones a repository to a local path based on the provided query parameters.
8384
@@ -107,8 +108,9 @@ async def clone_repo(config: CloneConfig) -> Tuple[bytes, bytes]:
107108
# Extract and validate query parameters
108109
url: str = config.url
109110
local_path: str = config.local_path
110-
commit: Optional[str] = config.commit
111-
branch: Optional[str] = config.branch
111+
commit: str | None = config.commit
112+
branch: str | None = config.branch
113+
112114

113115
if not url:
114116
raise ValueError("The 'url' parameter is required.")
@@ -131,7 +133,8 @@ async def clone_repo(config: CloneConfig) -> Tuple[bytes, bytes]:
131133
checkout_cmd = ["git", "-C", local_path, "checkout", commit]
132134
return await run_git_command(*checkout_cmd)
133135

134-
if branch and branch.lower() not in ('main', 'master'):
136+
if branch and branch.lower() not in ("main", "master"):
137+
135138
# Scenario 2: Clone a specific branch with shallow depth
136139
clone_cmd = ["git", "clone", "--depth=1", "--single-branch", "--branch", branch, url, local_path]
137140
return await run_git_command(*clone_cmd)

0 commit comments

Comments
 (0)