Skip to content

Commit

Permalink
Modernize Codebase and Enhance CI Workflow (#67)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
filipchristiansen authored Dec 29, 2024
1 parent 3c5e7e9 commit 848b9dc
Show file tree
Hide file tree
Showing 21 changed files with 398 additions and 342 deletions.
47 changes: 47 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: CI

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: true
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ["3.10", "3.11", "3.12", "3.13"]

steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Cache pip
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements*.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install dependencies
run: |
pip install --upgrade pip
pip install -r requirements-dev.txt
- name: Run tests
run: |
pytest
# Run pre-commit only on Python 3.13 + ubuntu.
- name: Run pre-commit hooks
if: ${{ matrix.python-version == '3.13' && matrix.os == 'ubuntu-latest' }}
run: |
pre-commit run --all-files
41 changes: 41 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Publish to PyPI

on:
release:
types: [created] # Trigger only when a release is created
workflow_dispatch: # Allows manual triggering of the workflow

jobs:
publish:
runs-on: ubuntu-latest

steps:
# Step 1: Check out the code
- name: Checkout code
uses: actions/checkout@v4

# Step 2: Set up Python
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: 3.13

# Step 3: Install dependencies for building and publishing
- name: Install build tools
run: |
pip install --upgrade pip
pip install build twine
# Step 4: Build the package
- name: Build the package
run: |
python -m build
# Step 5: Publish to PyPI
- name: Publish to PyPI
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
run: |
python -m twine check dist/*
python -m twine upload --skip-existing dist/*
33 changes: 0 additions & 33 deletions .github/workflows/unitest.yml

This file was deleted.

36 changes: 18 additions & 18 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,39 @@ repos:
hooks:
# Files
- id: check-added-large-files
description: 'Prevent large files from being committed.'
args: ['--maxkb=10000']
description: "Prevent large files from being committed."
args: ["--maxkb=10000"]
- id: check-case-conflict
description: 'Check for files that would conflict in case-insensitive filesystems.'
description: "Check for files that would conflict in case-insensitive filesystems."
- id: fix-byte-order-marker
description: 'Remove utf-8 byte order marker.'
description: "Remove utf-8 byte order marker."
- id: mixed-line-ending
description: 'Replace mixed line ending.'
description: "Replace mixed line ending."

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

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

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

# Python
- id: check-docstring-first
description: 'Check a common error of defining a docstring after code.'
description: "Check a common error of defining a docstring after code."
- id: requirements-txt-fixer
description: 'Sort entries in requirements.txt.'
description: "Sort entries in requirements.txt."

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

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

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

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

- repo: https://github.com/hadialqattan/pycln
rev: v2.4.0
hooks:
- id: pycln
description: 'Remove unused import statements.'
description: "Remove unused import statements."

- repo: https://github.com/djlint/djLint
rev: v1.36.4
Expand Down
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,3 @@ filter_files = true

[tool.black]
line-length = 119
skip-string-normalization = true
7 changes: 7 additions & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
-r requirements.txt
black
djlint
pre-commit
pylint
pytest
pytest-asyncio
5 changes: 0 additions & 5 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
black
click>=8.0.0
djlint
fastapi-analytics
fastapi[standard]
pre-commit
pytest
pytest-asyncio
python-dotenv
slowapi
starlette
Expand Down
20 changes: 9 additions & 11 deletions src/gitingest/cli.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import os
from typing import Optional, Tuple

import click

from gitingest.ignore_patterns import DEFAULT_IGNORE_PATTERNS
from gitingest.ingest import ingest
from gitingest.ingest_from_query import MAX_FILE_SIZE

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


@click.command()
@click.argument('source', type=str, required=True)
@click.option('--output', '-o', default=None, help='Output file path (default: <repo_name>.txt in current directory)')
@click.option('--max-size', '-s', default=MAX_FILE_SIZE, help='Maximum file size to process in bytes')
@click.option('--exclude-pattern', '-e', multiple=True, help='Patterns to exclude')
@click.option('--include-pattern', '-i', multiple=True, help='Patterns to include')
@click.argument("source", type=str, required=True)
@click.option("--output", "-o", default=None, help="Output file path (default: <repo_name>.txt in current directory)")
@click.option("--max-size", "-s", default=MAX_FILE_SIZE, help="Maximum file size to process in bytes")
@click.option("--exclude-pattern", "-e", multiple=True, help="Patterns to exclude")
@click.option("--include-pattern", "-i", multiple=True, help="Patterns to include")
def main(
source: str,
output: Optional[str],
output: str | None,
max_size: int,
exclude_pattern: Tuple[str, ...],
include_pattern: Tuple[str, ...],
exclude_pattern: tuple[str, ...],
include_pattern: tuple[str, ...],
) -> None:
"""Analyze a directory and create a text dump of its contents."""
try:
Expand All @@ -48,5 +46,5 @@ def main(
raise click.Abort()


if __name__ == '__main__':
if __name__ == "__main__":
main()
19 changes: 11 additions & 8 deletions src/gitingest/clone.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import asyncio
from dataclasses import dataclass
from typing import Optional, Tuple

from gitingest.utils import AsyncTimeoutError, async_timeout

Expand All @@ -11,8 +10,9 @@
class CloneConfig:
url: str
local_path: str
commit: Optional[str] = None
branch: Optional[str] = None
commit: str | None = None
branch: str | None = None



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


async def run_git_command(*args: str) -> Tuple[bytes, bytes]:
async def run_git_command(*args: str) -> tuple[bytes, bytes]:

"""
Executes a git command asynchronously and captures its output.
Expand Down Expand Up @@ -77,7 +78,7 @@ async def run_git_command(*args: str) -> Tuple[bytes, bytes]:


@async_timeout(CLONE_TIMEOUT)
async def clone_repo(config: CloneConfig) -> Tuple[bytes, bytes]:
async def clone_repo(config: CloneConfig) -> tuple[bytes, bytes]:
"""
Clones a repository to a local path based on the provided query parameters.
Expand Down Expand Up @@ -107,8 +108,9 @@ async def clone_repo(config: CloneConfig) -> Tuple[bytes, bytes]:
# Extract and validate query parameters
url: str = config.url
local_path: str = config.local_path
commit: Optional[str] = config.commit
branch: Optional[str] = config.branch
commit: str | None = config.commit
branch: str | None = config.branch


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

if branch and branch.lower() not in ('main', 'master'):
if branch and branch.lower() not in ("main", "master"):

# Scenario 2: Clone a specific branch with shallow depth
clone_cmd = ["git", "clone", "--depth=1", "--single-branch", "--branch", branch, url, local_path]
return await run_git_command(*clone_cmd)
Expand Down
Loading

0 comments on commit 848b9dc

Please sign in to comment.