Skip to content

Commit b791137

Browse files
Add rename script (#78)
1 parent dfa511e commit b791137

5 files changed

Lines changed: 74 additions & 15 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,3 +170,6 @@ cython_debug/
170170

171171
# PyPI configuration file
172172
.pypirc
173+
174+
# AI agent logs
175+
.[jJ]ules

Makefile

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,22 @@ DESCRIPTION ?= Python Project Template
55
AUTHOR ?= Amr Abed
66
EMAIL ?= amrabed
77
GITHUB ?= amrabed
8-
SOURCE ?= $(shell echo ${NAME} | tr '-' '_' | tr '[:upper:]' '[:lower:]')
98

109
.PHONY: help
1110
help: # Show help
1211
@grep -E '^[a-zA-Z_-]+:.*?# .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?# "}; {printf "\033[36m%-15s\033[0m %s\n", $$1, $$2}'
1312

1413
.PHONY: project
15-
project: # Rename project (run once)
16-
@if [ -d project ]; then mv project ${SOURCE}; fi
17-
@sed -i '' 's/^::: project\.app/::: ${SOURCE}\.app/' docs/reference/app.md
18-
@sed -i '' 's/^repo_name: .*/repo_name: ${GITHUB}\/${NAME}/' mkdocs.yml
19-
@sed -i '' 's/^repo_url: .*/repo_url: https:\/\/github.com\/${GITHUB}\/${NAME}/' mkdocs.yml
20-
@sed -i '' 's/^source = \[.*\]/source = \["${SOURCE}"\]/' pyproject.toml
21-
@sed -i '' 's/^app = "project\.app:main"/app = "${SOURCE}\.app:main"/' pyproject.toml
22-
@sed -i '' 's/^name = ".*"/name = "${SOURCE}"/' pyproject.toml
23-
@sed -i '' 's/^description = ".*"/description = "${DESCRIPTION}"/' pyproject.toml
24-
@sed -i '' 's/^authors = \[.*\]/authors = \["${AUTHOR} <${EMAIL}>"\]/' pyproject.toml
25-
@sed -i '' 's/^# .*/# ${DESCRIPTION}/' docs/README.md
26-
@sed -i '' 's/@.*/@${GITHUB}/' .github/CODEOWNERS
27-
@sed -i '' 's/^github: \[.*\]/github: \[${GITHUB}\]/' .github/FUNDING.yml
14+
project: uv # Rename project (run once)
15+
@uv run rename \
16+
--name '$(subst ','\'',$(NAME))' \
17+
--description '$(subst ','\'',$(DESCRIPTION))' \
18+
--author '$(subst ','\'',$(AUTHOR))' \
19+
--email '$(subst ','\'',$(EMAIL))' \
20+
--github '$(subst ','\'',$(GITHUB))'
2821

2922
uv: # Install uv
30-
pipx install -f uv
23+
@command -v uv >/dev/null 2>&1 || pipx install uv
3124

3225
venv: # Create and activate virtual environment and install dependencies
3326
uv sync

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ dependencies = [
1212

1313
[project.scripts]
1414
app = "project.app:main"
15+
rename = "scripts.rename:main"
1516

1617
[dependency-groups]
1718
dev = [

scripts/__init__.py

Whitespace-only changes.

scripts/rename.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import os
2+
import re
3+
import shutil
4+
from pathlib import Path
5+
6+
from click import ClickException, UsageError, command, echo, option
7+
8+
9+
@command()
10+
@option("--name", required=True, help="Project new name")
11+
@option("--description", required=True, help="Project short description")
12+
@option("--author", required=True, help="Author name")
13+
@option("--email", required=True, help="Author email")
14+
@option("--github", required=True, help="GitHub username")
15+
def main(name: str, description: str, author: str, email: str, github: str):
16+
# Validate name to prevent directory traversal or other injection
17+
if not re.match(r"^[a-zA-Z0-9_-]+$", name):
18+
raise UsageError(
19+
f"Invalid project name '{name}'. Only alphanumeric characters, dashes, and underscores are allowed."
20+
)
21+
22+
source = name.replace("-", "_").lower()
23+
24+
echo(f"Initializing project '{name}' (source: '{source}')...")
25+
26+
# 1. Rename project directory
27+
if os.path.isdir("project"):
28+
shutil.move("project", source)
29+
elif not os.path.isdir(source):
30+
raise ClickException(f"Error: Neither 'project' nor '{source}' directory found.")
31+
32+
# 2. File modifications
33+
replacements = [
34+
("docs/reference/app.md", r"^::: project\.app", f"::: {source}.app"),
35+
("mkdocs.yml", r"^repo_name: .*", f"repo_name: {github}/{name}"),
36+
("mkdocs.yml", r"^repo_url: .*", f"repo_url: https://github.com/{github}/{name}"),
37+
("pyproject.toml", r"^source = \[.*\]", f'source = ["{source}"]'),
38+
("pyproject.toml", r'^app = "project\.app:main"', f'app = "{source}.app:main"'),
39+
("pyproject.toml", r'^name = ".*"', f'name = "{source}"'),
40+
("pyproject.toml", r'^description = ".*"', f'description = "{description}"'),
41+
("pyproject.toml", r"^authors = \[.*\]", f'authors = ["{author} <{email}>"]'),
42+
("docs/README.md", r"^# .*", f"# {description}"),
43+
(".github/CODEOWNERS", r"@.*", f"@{github}"),
44+
(".github/FUNDING.yml", r"^github: \[.*\]", f"github: [{github}]"),
45+
]
46+
47+
for filepath, pattern, replacement in replacements:
48+
path = Path(filepath)
49+
if not path.exists():
50+
echo(f"Warning: File {filepath} not found, skipping.")
51+
continue
52+
53+
content = path.read_text()
54+
new_content = re.sub(pattern, replacement, content, flags=re.MULTILINE)
55+
path.write_text(new_content)
56+
echo(f"Updated {filepath}")
57+
58+
echo("Project initialization complete.")
59+
60+
61+
if __name__ == "__main__":
62+
main()

0 commit comments

Comments
 (0)