Skip to content

Commit cab805f

Browse files
feat: update template
1 parent d68fb94 commit cab805f

File tree

18 files changed

+510
-247
lines changed

18 files changed

+510
-247
lines changed

repo_scaffold/cli.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,12 @@ def list():
120120
type=click.Path(file_okay=False, dir_okay=True, path_type=Path),
121121
help="Directory where the project will be created",
122122
)
123-
def create(template: str, output_dir: Path):
123+
@click.option(
124+
"--no-input",
125+
is_flag=True,
126+
help="Do not prompt for parameters and only use cookiecutter.json file content",
127+
)
128+
def create(template: str, output_dir: Path, no_input: bool): # noqa: D417
124129
"""Create a new project from a template.
125130
126131
Creates a new project based on the specified template. If no template is specified,
@@ -176,7 +181,7 @@ def create(template: str, output_dir: Path):
176181
cookiecutter(
177182
template=template_path,
178183
output_dir=str(output_dir),
179-
no_input=False, # 启用交互式输入,让 cookiecutter 处理所有选项
184+
no_input=no_input, # 根据用户选择决定是否启用交互式输入
180185
)
181186

182187

repo_scaffold/templates/template-python/cookiecutter.json

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,18 @@
55
"github_username": "ShawnDen-coder",
66
"project_slug": "{{ cookiecutter.repo_name.strip().lower().replace('-', '_') }}",
77
"description": "A short description of the project.",
8-
"min_python_version": ["3.9", "3.10", "3.11", "3.12", "3.13","3.14"],
9-
"max_python_version": ["3.9", "3.10", "3.11", "3.12", "3.13","3.14"],
10-
"use_docker": ["yes", "no"],
8+
"min_python_version": ["3.10", "3.9", "3.11", "3.12", "3.13","3.14"],
9+
"max_python_version": ["3.12", "3.9", "3.10", "3.11", "3.13","3.14"],
10+
"use_podman": ["yes", "no"],
1111
"include_cli": ["yes", "no"],
12-
"use_github_actions": ["yes", "no"],
12+
"use_github_actions": "yes",
13+
"pypi_server_url": "https://pypiserver.shawndeng.cc/simple/",
1314
"__prompts__": {
1415
"repo_name": "项目名称 (my-awesome-project)",
15-
"full_name": "你的全名",
16-
"email": "你的邮箱地址",
17-
"github_username": "你的 GitHub 用户名",
18-
"project_slug": "Python 包名 (用于 import)",
1916
"description": "项目简短描述",
2017
"min_python_version": "最低支持的 Python 版本 (这将影响测试范围和依赖兼容性)",
2118
"max_python_version": "最高支持的 Python 版本",
22-
"use_docker": "是否添加 Docker 支持?",
23-
"include_cli": "是否添加命令行界面?",
24-
"use_github_actions": "是否添加 GitHub Actions 支持?"
19+
"use_podman": "是否添加 Podman 支持?",
20+
"include_cli": "是否添加命令行界面?"
2521
}
2622
}
Lines changed: 139 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,147 @@
1+
"""Post-generation project setup and cleanup script.
2+
3+
This script runs after cookiecutter generates the project template.
4+
It removes unnecessary files based on user choices and initializes the project.
5+
"""
6+
17
import os
28
import shutil
39
import subprocess
10+
import sys
11+
from pathlib import Path
12+
from typing import List, Union
13+
14+
15+
class ProjectCleaner:
16+
"""Handles removal of unnecessary files and directories based on cookiecutter choices."""
17+
18+
def __init__(self):
19+
self.project_slug = "{{cookiecutter.project_slug}}"
20+
self.use_cli = "{{cookiecutter.include_cli}}" == "yes"
21+
self.use_github_actions = "{{cookiecutter.use_github_actions}}" == "yes"
22+
self.use_podman = "{{cookiecutter.use_podman}}" == "yes"
23+
24+
def _safe_remove(self, path: Union[str, Path]) -> bool:
25+
"""Safely remove a file or directory.
26+
27+
Args:
28+
path: Path to remove
29+
30+
Returns:
31+
bool: True if removed successfully, False otherwise
32+
"""
33+
try:
34+
path = Path(path)
35+
if not path.exists():
36+
return False
37+
38+
if path.is_file():
39+
path.unlink()
40+
print(f"Removed file: {path}")
41+
elif path.is_dir():
42+
shutil.rmtree(path)
43+
print(f"Removed directory: {path}")
44+
return True
45+
except Exception as e:
46+
print(f"Warning: Failed to remove {path}: {e}")
47+
return False
48+
49+
def _remove_files(self, files: List[Union[str, Path]]) -> None:
50+
"""Remove multiple files or directories.
51+
52+
Args:
53+
files: List of file/directory paths to remove
54+
"""
55+
for file_path in files:
56+
self._safe_remove(file_path)
57+
58+
def clean_cli_files(self) -> None:
59+
"""Remove CLI related files if CLI is not needed."""
60+
if self.use_cli:
61+
return
62+
63+
cli_files = [
64+
Path(self.project_slug) / "cli.py"
65+
]
66+
print("Removing CLI files...")
67+
self._remove_files(cli_files)
68+
69+
def clean_container_files(self) -> None:
70+
"""Remove container related files if Podman is not used."""
71+
if self.use_podman:
72+
return
73+
74+
container_files = [
75+
".dockerignore",
76+
"container",
77+
Path(".github") / "workflows" / "container-release.yaml"
78+
]
79+
print("Removing container files...")
80+
self._remove_files(container_files)
81+
82+
def clean_github_actions_files(self) -> None:
83+
"""Remove GitHub Actions and documentation files if not needed."""
84+
if self.use_github_actions:
85+
return
86+
87+
github_files = [
88+
".github",
89+
"mkdocs.yml",
90+
"docs"
91+
]
92+
print("Removing GitHub Actions and documentation files...")
93+
self._remove_files(github_files)
94+
95+
96+
class ProjectInitializer:
97+
"""Handles project initialization tasks."""
98+
99+
def __init__(self):
100+
self.project_slug = "{{cookiecutter.project_slug}}"
101+
102+
def setup_environment(self) -> None:
103+
"""Initialize project dependencies and environment."""
104+
project_dir = Path(self.project_slug).resolve()
105+
106+
if not project_dir.exists():
107+
print(f"Error: Project directory {project_dir} does not exist")
108+
sys.exit(1)
109+
110+
print(f"Changing to project directory: {project_dir}")
111+
os.chdir(project_dir)
112+
113+
try:
114+
print("Installing project dependencies...")
115+
subprocess.run(["task", "init"], check=True)
116+
print("✅ Dependencies installed successfully")
117+
except subprocess.CalledProcessError as e:
118+
print(f"❌ Failed to install dependencies: {e}")
119+
sys.exit(1)
120+
except FileNotFoundError:
121+
print("❌ uv not found. Please install uv first: https://docs.astral.sh/uv/")
122+
sys.exit(1)
123+
124+
125+
def main() -> None:
126+
"""Main execution function."""
127+
print("🚀 Starting post-generation project setup...")
128+
129+
# Initialize cleaner and perform cleanup
130+
cleaner = ProjectCleaner()
131+
132+
print("\n📁 Cleaning up unnecessary files...")
133+
cleaner.clean_cli_files()
134+
cleaner.clean_container_files()
135+
cleaner.clean_github_actions_files()
4136

137+
# Initialize project
138+
print("\n🔧 Initializing project...")
139+
initializer = ProjectInitializer()
140+
initializer.setup_environment()
5141

6-
def remove_cli():
7-
"""Remove CLI related files if not needed."""
8-
cli_file = os.path.join("{{cookiecutter.project_slug}}", "cli.py")
9-
if os.path.exists(cli_file):
10-
os.remove(cli_file)
11-
12-
def remove_docker():
13-
"""Remove GitHub Actions configuration if not needed."""
14-
file_name = [".dockerignore", "docker", ".github/workflows/docker_release.yaml"]
15-
if "{{cookiecutter.use_github_actions}}" == "no":
16-
for item in file_name:
17-
if os.path.exists(item):
18-
if os.path.isfile(item):
19-
os.remove(item)
20-
elif os.path.isdir(item):
21-
shutil.rmtree(item)
22-
23-
24-
def remove_github_actions():
25-
"""Remove GitHub Actions configuration if not needed."""
26-
if "{{cookiecutter.use_github_actions}}" == "no":
27-
github_dir = os.path.join(".github")
28-
if os.path.exists(github_dir):
29-
shutil.rmtree(github_dir)
30-
31-
32-
def remove_docs():
33-
"""Remove documentation related files if GitHub Actions is not used."""
34-
if "{{cookiecutter.use_github_actions}}" == "no":
35-
# 删除 mkdocs.yml
36-
if os.path.exists("mkdocs.yml"):
37-
os.remove("mkdocs.yml")
38-
# 删除 docs 目录
39-
docs_dir = "docs"
40-
if os.path.exists(docs_dir):
41-
shutil.rmtree(docs_dir)
42-
43-
44-
def init_project_depends():
45-
"""Initialize project dependencies using uv."""
46-
project_dir = os.path.abspath("{{cookiecutter.project_slug}}")
47-
os.chdir(project_dir)
48-
49-
# 安装基础开发依赖
50-
subprocess.run(["uv", "sync"], check=True)
142+
print("\n✨ Project setup completed successfully!")
143+
print(f"📂 Your project is ready at: {{cookiecutter.project_slug}}")
51144

52145

53146
if __name__ == "__main__":
54-
if "{{cookiecutter.include_cli}}" == "no":
55-
remove_cli()
56-
57-
if "{{cookiecutter.use_github_actions}}" == "no":
58-
remove_github_actions()
59-
remove_docs()
60-
61-
if "{{cookiecutter.use_docker}}" == "no":
62-
remove_docker()
63-
64-
init_project_depends()
147+
main()

repo_scaffold/templates/template-python/{{cookiecutter.project_slug}}/.github/workflows/bump_version.yaml

Lines changed: 0 additions & 25 deletions
This file was deleted.
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
name: ✅ Code Quality & Tests
2+
on:
3+
push:
4+
branches:
5+
- main
6+
- master
7+
pull_request:
8+
workflow_dispatch:
9+
permissions:
10+
contents: read
11+
env:
12+
{% raw %}UV_INDEX_HOMELAB_USERNAME: ${{ secrets.PYPI_SERVER_USERNAME }}
13+
UV_INDEX_HOMELAB_PASSWORD: ${{ secrets.PYPI_SERVER_PASSWORD }}
14+
PYPI_SERVER_USERNAME: ${{ secrets.PYPI_SERVER_USERNAME }}
15+
PYPI_SERVER_PASSWORD: ${{ secrets.PYPI_SERVER_PASSWORD }}{% endraw %}
16+
jobs:
17+
check:
18+
runs-on: ubuntu-latest
19+
steps:
20+
- name: Checkout code
21+
uses: actions/checkout@v4
22+
- name: Install Task
23+
uses: arduino/setup-task@v2
24+
- name: Install uv
25+
uses: astral-sh/setup-uv@v5
26+
- name: Install dependencies
27+
run: task init
28+
- name: Run lint checks
29+
id: lint
30+
run: task lint
31+
- name: Run test all versions
32+
run: task test:all
33+
{%- if cookiecutter.use_podman == 'yes' %}
34+
35+
container-build-test:
36+
name: 🐳 Multi-platform Container Build Test
37+
needs: check
38+
runs-on: ubuntu-latest
39+
steps:
40+
- name: Check out the repo
41+
uses: actions/checkout@v4
42+
- name: Check if Dockerfile exists
43+
id: dockerfile-check
44+
run: |
45+
if [ -f "./container/Dockerfile" ]; then
46+
echo "dockerfile-exists=true" >> $GITHUB_OUTPUT
47+
else
48+
echo "dockerfile-exists=false" >> $GITHUB_OUTPUT
49+
echo "Dockerfile not found, skipping container build test"
50+
fi
51+
- name: Set up QEMU
52+
if: steps.dockerfile-check.outputs.dockerfile-exists == 'true'
53+
uses: docker/setup-qemu-action@v3
54+
- name: Test Build Container Image (no push)
55+
if: steps.dockerfile-check.outputs.dockerfile-exists == 'true'
56+
id: build-image
57+
uses: redhat-actions/buildah-build@v2
58+
with:
59+
{% raw %}image: ${{ github.repository }}-test
60+
tags: test-${{ github.sha }}{% endraw %}
61+
containerfiles: ./container/Dockerfile
62+
platforms: linux/amd64,linux/arm64
63+
build-args: |-
64+
{% raw %}PYPI_SERVER_USERNAME=${{ env.PYPI_SERVER_USERNAME }}
65+
PYPI_SERVER_PASSWORD=${{ env.PYPI_SERVER_PASSWORD }}{% endraw %}
66+
{%- endif %}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: 🐳 Docker Release
2+
on:
3+
release:
4+
types: [published]
5+
workflow_dispatch:
6+
jobs:
7+
push_to_registry:
8+
name: Push Container image
9+
runs-on: ubuntu-latest
10+
steps:
11+
- name: Check out the repo
12+
uses: actions/checkout@v4
13+
- name: Set up QEMU
14+
uses: docker/setup-qemu-action@v3
15+
- name: Login to GitHub Container Registry
16+
uses: redhat-actions/podman-login@v1
17+
with:
18+
registry: ghcr.io
19+
{% raw %}username: ${{ github.actor }}
20+
password: ${{ secrets.PERSONAL_ACCESS_TOKEN }}{% endraw %}
21+
- name: Build Image
22+
id: build-image
23+
uses: redhat-actions/buildah-build@v2
24+
with:
25+
{% raw %}image: ${{ github.repository }}
26+
tags: latest ${{ github.sha }} ${{ github.ref_name }}{% endraw %}
27+
containerfiles: ./container/Dockerfile
28+
platforms: linux/amd64,linux/arm64
29+
build-args: |
30+
{% raw %}PYPI_SERVER_USERNAME=${{ secrets.PYPI_SERVER_USERNAME }}
31+
PYPI_SERVER_PASSWORD=${{ secrets.PYPI_SERVER_PASSWORD }}{% endraw %}
32+
- name: Push To Registry
33+
uses: redhat-actions/push-to-registry@v2
34+
with:
35+
{% raw %}image: ${{ steps.build-image.outputs.image }}
36+
tags: ${{ steps.build-image.outputs.tags }}{% endraw %}
37+
registry: ghcr.io

0 commit comments

Comments
 (0)