Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 19 additions & 7 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,34 @@ jobs:
run: |
uv pip compile pyproject.toml -o requirements.txt
uv pip compile pyproject.toml --extra dev -o requirements-dev.txt
uv venv
uv pip sync requirements.txt requirements-dev.txt

- name: Run MyPy
run: uv pip run mypy src

- name: Determine module name
id: module
run: |
if [ -d "src" ]; then
echo "name=src" >> $GITHUB_OUTPUT
else
MODULE_NAME=$(basename $(find . -maxdepth 1 -type d -not -path "*/\.*" -not -path "./tests" -not -path "./scripts" -not -path "./docker" -not -path "." | sort | head -1))
echo "name=$MODULE_NAME" >> $GITHUB_OUTPUT
fi

- name: Run Linter
run: uv pip run ruff check src
run: uv run -m ruff check --fix ${{ steps.module.outputs.name }}

- name: Run Formatter
run: uv pip run ruff format src
run: uv run -m ruff format ${{ steps.module.outputs.name }}

- name: Run Tests
run: uv pip run pytest tests --cov=src --cov-report=term-missing --cov-report=xml
run: uv run -m pytest tests --cov=${{ steps.module.outputs.name }} --cov-report=term-missing --cov-report=xml

- name: Run MyPy
run: uv run -m mypy ${{ steps.module.outputs.name }}

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
continue-on-error: true
with:
files: coverage.xml
fail_ci_if_error: true
fail_ci_if_error: false
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ docker/.env
.pytest_cache/*
.ruff_cache/*
.aider*
CLAUDE.md
12 changes: 6 additions & 6 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
repos:
- repo: local
hooks:
- id: mypy
name: Run MyPy
entry: make mypy
language: system
always_run: true
pass_filenames: false
- id: lint
name: Run Linter
entry: make lint
Expand All @@ -25,3 +19,9 @@ repos:
language: system
always_run: true
pass_filenames: false
- id: mypy
name: Run MyPy
entry: make mypy
language: system
always_run: true
pass_filenames: false
15 changes: 9 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
.PHONY: compile-deps setup clean-pyc clean-test clean-venv clean test mypy lint format check clean-example dev-env refresh-containers rebuild-images build-image push-image

# Module name - will be updated by init script
MODULE_NAME := src

# Development Setup
#################
compile-deps: # Compile dependencies from pyproject.toml
Expand Down Expand Up @@ -46,18 +49,18 @@ clean: clean-pyc clean-test clean-venv
# Testing and Quality Checks
#########################
test: setup # Run pytest with coverage
uv run -m pytest tests --cov=src --cov-report=term-missing
uv run -m pytest tests --cov=$(MODULE_NAME) --cov-report=term-missing

mypy: setup # Run type checking
uv run -m mypy src
uv run -m mypy $(MODULE_NAME)

lint: setup # Run ruff linter
uv run -m ruff check src
lint: setup # Run ruff linter with auto-fix
uv run -m ruff check --fix $(MODULE_NAME)

format: setup # Run ruff formatter
uv run -m ruff format src
uv run -m ruff format $(MODULE_NAME)

check: setup test mypy lint format # Run all quality checks
check: setup lint format test mypy # Run all quality checks

# Project Management
##################
Expand Down
114 changes: 100 additions & 14 deletions scripts/init_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,15 @@ def main() -> None:
"Author email", get_git_config("email") or "[email protected]"
)

# Update project information
print("📝 Updating project configuration...")
update_pyproject_toml(
project_name,
project_description,
author_name,
author_email
)

# Handle example code
code_choice = prompt_with_default(
"How would you like to handle example code?\n"
Expand All @@ -84,36 +93,101 @@ def main() -> None:
"Choose option", "1"
)

# Create module directory with project name (replacing src)
project_module_name = project_name.replace("-", "_").lower()

# Always update the Makefile to use the new module name
print(f"🔧 Updating Makefile to use module name: {project_module_name}")
makefile_path = Path("Makefile")
with open(makefile_path, "r") as f:
makefile_content = f.read()

# Replace module name in Makefile
updated_makefile = makefile_content.replace("MODULE_NAME := src", f"MODULE_NAME := {project_module_name}")

with open(makefile_path, "w") as f:
f.write(updated_makefile)

# Always update pyproject.toml to point to the new module directory
print(f"📦 Updating pyproject.toml for module: {project_module_name}")
pyproject_path = Path("pyproject.toml")
with open(pyproject_path, "rb") as f:
config = tomli.load(f)

# Update packages from src to new module name
if "tool" in config and "hatch" in config["tool"] and "build" in config["tool"]["hatch"] and "targets" in config["tool"]["hatch"]["build"] and "wheel" in config["tool"]["hatch"]["build"]["targets"]:
config["tool"]["hatch"]["build"]["targets"]["wheel"]["packages"] = [project_module_name]

with open(pyproject_path, "wb") as f:
tomli_w.dump(config, f)

# Create the new module directory if it doesn't exist
if not os.path.exists(project_module_name):
print(f"📁 Creating module directory: {project_module_name}")
os.mkdir(project_module_name)
# Create __init__.py
with open(f"{project_module_name}/__init__.py", "w") as f:
f.write(f'"""Main package for {project_name}."""\n')

# Copy src content to new module directory if src exists
if os.path.exists("src") and project_module_name != "src":
print(f"📦 Copying content from src to {project_module_name}...")
for item in os.listdir("src"):
src_path = os.path.join("src", item)
dest_path = os.path.join(project_module_name, item)

if os.path.isfile(src_path):
with open(src_path, "r") as src_file:
content = src_file.read()
with open(dest_path, "w") as dest_file:
dest_file.write(content)

# Remove the old src directory after copying
print("🗑️ Removing old src directory...")
run_command("rm -rf src")

if code_choice == "2":
print("📝 Creating minimal placeholder test...")
# Create minimal src module
with open("src/example.py", "w") as f:
# Create minimal module
with open(f"{project_module_name}/example.py", "w") as f:
f.write("""def add(a: int, b: int) -> int:
\"\"\"Add two numbers.\"\"\"
return a + b
""")

# Create minimal test
with open("tests/test_example.py", "w") as f:
f.write("""from src.example import add
f.write(f"""from {project_module_name}.example import add

def test_add():
assert add(1, 2) == 3
""")
elif code_choice == "3":
print("🧹 Removing all example code...")
run_command("make clean-example")
# Create __init__.py in tests
with open("tests/__init__.py", "w") as f:
f.write("")
else:
print("📚 Keeping example code for reference...")
print("📚 Updating example code imports for new module name...")
# Update example.py to use new module name
if os.path.exists("src/example.py"):
with open("src/example.py", "r") as f:
example_content = f.read()
# Save it to new module directory
with open(f"{project_module_name}/example.py", "w") as f:
f.write(example_content)

# Update test imports
if os.path.exists("tests/test_example.py"):
with open("tests/test_example.py", "r") as f:
test_content = f.read()
updated_test = test_content.replace("from src.", f"from {project_module_name}.")
with open("tests/test_example.py", "w") as f:
f.write(updated_test)

# Update pyproject.toml
print("📝 Updating project configuration...")
update_pyproject_toml(
project_name,
project_description,
author_name,
author_email
)
# Update already happened above, fix the duplicate
# The configuration has already been updated above

# Get current directory name and handle renaming
current_dir = os.path.basename(os.getcwd())
Expand All @@ -124,6 +198,18 @@ def test_add():
if os.path.exists(new_dir):
print(f"⚠️ Directory {project_name} already exists. Keeping current directory name.")
else:
# Update source code directory references in Makefile
makefile_path = Path("Makefile")
with open(makefile_path, "r") as f:
makefile_content = f.read()

# Replace any hardcoded references to python-collab-template in the Makefile
updated_makefile = makefile_content.replace("python-collab-template", project_name)

with open(makefile_path, "w") as f:
f.write(updated_makefile)

# Now rename the directory
os.chdir(parent_dir)
os.rename(current_dir, project_name)
os.chdir(project_name)
Expand Down Expand Up @@ -155,9 +241,9 @@ def test_add():
else:
print("⏩ Skipping pre-commit hooks setup")

# Initial commit
# Initial commit without running pre-commit hooks
run_command("git add .")
run_command('git commit -m "feat: Initial project setup"')
run_command('git commit -m "feat: Initial project setup" --no-verify')

print("✨ Project initialized successfully!")
print("""
Expand Down