afterpython 0.1.0__py3-none-any.whl → 0.1.0.dev5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- afterpython/{templates/pre-commit-config-template.yaml → .pre-commit-config.yaml} +1 -1
- afterpython/_io/toml.py +3 -5
- afterpython/_paths.py +1 -1
- afterpython/_typing.py +2 -1
- afterpython/builders/__init__.py +3 -12
- afterpython/builders/metadata.py +3 -7
- afterpython/builders/molab.py +99 -0
- afterpython/cli/__init__.py +1 -0
- afterpython/cli/commands/build.py +66 -136
- afterpython/cli/commands/bump.py +0 -30
- afterpython/cli/commands/check.py +1 -1
- afterpython/cli/commands/clean.py +3 -4
- afterpython/cli/commands/commit.py +3 -21
- afterpython/cli/commands/commitizen.py +1 -1
- afterpython/cli/commands/dev.py +13 -80
- afterpython/cli/commands/format.py +1 -1
- afterpython/cli/commands/init.py +39 -48
- afterpython/cli/commands/install.py +2 -8
- afterpython/cli/commands/pre_commit.py +1 -1
- afterpython/cli/commands/preview.py +1 -1
- afterpython/cli/commands/start.py +4 -8
- afterpython/cli/commands/sync.py +21 -65
- afterpython/cli/commands/update.py +8 -31
- afterpython/cli/main.py +14 -75
- afterpython/const.py +2 -1
- afterpython/cz.toml +27 -0
- afterpython/pcu.py +1 -3
- afterpython/templates/deploy-workflow-template.yml +7 -5
- afterpython/templates/release-workflow-template.yml +3 -3
- afterpython/templates/ruff-template.toml +1 -1
- afterpython/tools/_afterpython.py +10 -73
- afterpython/tools/_git.py +2 -28
- afterpython/tools/commitizen.py +45 -5
- afterpython/tools/github_actions.py +53 -23
- afterpython/tools/myst.py +38 -82
- afterpython/tools/pre_commit.py +31 -6
- afterpython/tools/pyproject.py +2 -40
- afterpython/utils.py +3 -87
- {afterpython-0.1.0.dist-info → afterpython-0.1.0.dev5.dist-info}/METADATA +13 -39
- afterpython-0.1.0.dev5.dist-info/RECORD +47 -0
- {afterpython-0.1.0.dist-info → afterpython-0.1.0.dev5.dist-info}/WHEEL +1 -1
- afterpython/builders/content_json.py +0 -131
- afterpython/builders/index_md.py +0 -94
- afterpython/builders/jupyter_notebook.py +0 -293
- afterpython/builders/markdown.py +0 -271
- afterpython/cli/commands/add.py +0 -67
- afterpython/cli/commands/init_branch_rules.py +0 -9
- afterpython/cli/commands/lock.py +0 -23
- afterpython/cli/commands/remove.py +0 -67
- afterpython/templates/ci-workflow-template.yml +0 -197
- afterpython/templates/cz-template.toml +0 -7
- afterpython/templates/dependabot-template.yml +0 -30
- afterpython/tools/branch_rules.py +0 -106
- afterpython-0.1.0.dist-info/RECORD +0 -57
- {afterpython-0.1.0.dist-info → afterpython-0.1.0.dev5.dist-info}/entry_points.txt +0 -0
afterpython/cli/main.py
CHANGED
|
@@ -1,89 +1,32 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import subprocess
|
|
3
|
-
from collections import defaultdict
|
|
4
3
|
|
|
5
4
|
import click
|
|
6
|
-
from dotenv import find_dotenv, load_dotenv
|
|
7
5
|
from trogon import tui
|
|
6
|
+
from dotenv import load_dotenv, find_dotenv
|
|
8
7
|
|
|
9
8
|
import afterpython as ap
|
|
10
9
|
from afterpython import __version__
|
|
11
|
-
from afterpython.cli.commands.
|
|
10
|
+
from afterpython.cli.commands.init import init
|
|
12
11
|
from afterpython.cli.commands.build import build
|
|
13
|
-
from afterpython.cli.commands.bump import bump
|
|
14
|
-
from afterpython.cli.commands.check import check
|
|
15
|
-
from afterpython.cli.commands.clean import clean
|
|
16
|
-
from afterpython.cli.commands.commit import commit
|
|
17
|
-
from afterpython.cli.commands.commitizen import commitizen
|
|
18
12
|
from afterpython.cli.commands.dev import dev
|
|
19
|
-
from afterpython.cli.commands.
|
|
20
|
-
from afterpython.cli.commands.
|
|
21
|
-
from afterpython.cli.commands.init_branch_rules import init_branch_rules
|
|
13
|
+
from afterpython.cli.commands.update import update
|
|
14
|
+
from afterpython.cli.commands.check import check
|
|
22
15
|
from afterpython.cli.commands.install import install
|
|
23
|
-
from afterpython.cli.commands.
|
|
24
|
-
from afterpython.cli.commands.
|
|
16
|
+
from afterpython.cli.commands.format import format
|
|
17
|
+
from afterpython.cli.commands.sync import sync
|
|
18
|
+
from afterpython.cli.commands.start import start, doc, blog, tutorial, example, guide
|
|
25
19
|
from afterpython.cli.commands.preview import preview
|
|
20
|
+
from afterpython.cli.commands.clean import clean
|
|
21
|
+
from afterpython.cli.commands.pre_commit import pre_commit
|
|
22
|
+
from afterpython.cli.commands.commitizen import commitizen
|
|
23
|
+
from afterpython.cli.commands.commit import commit
|
|
24
|
+
from afterpython.cli.commands.bump import bump
|
|
26
25
|
from afterpython.cli.commands.release import release
|
|
27
|
-
from afterpython.cli.commands.remove import remove
|
|
28
|
-
from afterpython.cli.commands.start import blog, doc, example, guide, start, tutorial
|
|
29
|
-
from afterpython.cli.commands.sync import sync
|
|
30
|
-
from afterpython.cli.commands.update import update
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
class AliasGroup(click.Group):
|
|
34
|
-
"""Custom group that displays command aliases together."""
|
|
35
|
-
|
|
36
|
-
def __init__(self, *args, **kwargs):
|
|
37
|
-
super().__init__(*args, **kwargs)
|
|
38
|
-
self._aliases = defaultdict(list) # Maps primary command -> list of aliases
|
|
39
|
-
self._alias_to_primary = {} # Maps alias -> primary command name
|
|
40
|
-
|
|
41
|
-
def add_command(self, cmd, name=None):
|
|
42
|
-
"""Track aliases when the same command is added with different names."""
|
|
43
|
-
name = name or cmd.name
|
|
44
|
-
|
|
45
|
-
# Check if this command object was already added under a different name
|
|
46
|
-
for existing_name, existing_cmd in self.commands.items():
|
|
47
|
-
if existing_cmd is cmd and existing_name != name:
|
|
48
|
-
# This is an alias
|
|
49
|
-
self._aliases[existing_name].append(name)
|
|
50
|
-
self._alias_to_primary[name] = existing_name
|
|
51
|
-
break
|
|
52
|
-
|
|
53
|
-
super().add_command(cmd, name)
|
|
54
|
-
|
|
55
|
-
def format_commands(self, ctx, formatter):
|
|
56
|
-
"""Format commands with aliases shown together."""
|
|
57
|
-
commands = []
|
|
58
|
-
for subcommand in self.list_commands(ctx):
|
|
59
|
-
# Skip aliases - they'll be shown with their primary command
|
|
60
|
-
if subcommand in self._alias_to_primary:
|
|
61
|
-
continue
|
|
62
|
-
|
|
63
|
-
cmd = self.get_command(ctx, subcommand)
|
|
64
|
-
if cmd is None:
|
|
65
|
-
continue
|
|
66
|
-
|
|
67
|
-
# Skip hidden commands
|
|
68
|
-
if cmd.hidden:
|
|
69
|
-
continue
|
|
70
|
-
|
|
71
|
-
help_text = cmd.get_short_help_str(limit=formatter.width)
|
|
72
|
-
|
|
73
|
-
# Build command name with aliases
|
|
74
|
-
aliases = self._aliases.get(subcommand, [])
|
|
75
|
-
if aliases:
|
|
76
|
-
subcommand = f"{subcommand}, {', '.join(aliases)}"
|
|
77
|
-
|
|
78
|
-
commands.append((subcommand, help_text))
|
|
79
|
-
|
|
80
|
-
if commands:
|
|
81
|
-
with formatter.section("Commands"):
|
|
82
|
-
formatter.write_dl(commands)
|
|
83
26
|
|
|
84
27
|
|
|
85
28
|
@tui(command="tui", help="Open terminal UI")
|
|
86
|
-
@click.group(
|
|
29
|
+
@click.group(context_settings={"help_option_names": ["-h", "--help"]})
|
|
87
30
|
@click.pass_context
|
|
88
31
|
@click.version_option(version=__version__)
|
|
89
32
|
def afterpython_group(ctx):
|
|
@@ -107,7 +50,6 @@ afterpython_group.add_command(build)
|
|
|
107
50
|
afterpython_group.add_command(dev)
|
|
108
51
|
afterpython_group.add_command(update)
|
|
109
52
|
afterpython_group.add_command(check)
|
|
110
|
-
afterpython_group.add_command(check, name="lint")
|
|
111
53
|
afterpython_group.add_command(format)
|
|
112
54
|
afterpython_group.add_command(sync)
|
|
113
55
|
afterpython_group.add_command(start)
|
|
@@ -121,12 +63,9 @@ afterpython_group.add_command(preview)
|
|
|
121
63
|
afterpython_group.add_command(clean)
|
|
122
64
|
afterpython_group.add_command(pre_commit)
|
|
123
65
|
afterpython_group.add_command(pre_commit, name="pc")
|
|
66
|
+
afterpython_group.add_command(pre_commit, name="precommit")
|
|
124
67
|
afterpython_group.add_command(commitizen)
|
|
125
68
|
afterpython_group.add_command(commitizen, name="cz")
|
|
126
69
|
afterpython_group.add_command(commit)
|
|
127
70
|
afterpython_group.add_command(bump)
|
|
128
71
|
afterpython_group.add_command(release)
|
|
129
|
-
afterpython_group.add_command(init_branch_rules)
|
|
130
|
-
afterpython_group.add_command(add)
|
|
131
|
-
afterpython_group.add_command(remove)
|
|
132
|
-
afterpython_group.add_command(lock)
|
afterpython/const.py
CHANGED
afterpython/cz.toml
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[tool.commitizen]
|
|
2
|
+
name = "cz_conventional_commits"
|
|
3
|
+
# name = "cz_customize"
|
|
4
|
+
tag_format = "v$version"
|
|
5
|
+
version_scheme = "pep440"
|
|
6
|
+
version_provider = "uv"
|
|
7
|
+
update_changelog_on_bump = false
|
|
8
|
+
major_version_zero = true
|
|
9
|
+
|
|
10
|
+
# FIXME: commitizen can't even add a few custom types like "chore" and "wip", skip it for now
|
|
11
|
+
# [[tool.commitizen.customize.questions]]
|
|
12
|
+
# type = "list"
|
|
13
|
+
# name = "change_type"
|
|
14
|
+
# choices = [
|
|
15
|
+
# {value = "fix", name = "fix: A bug fix. Correlates with PATCH in SemVer"},
|
|
16
|
+
# {value = "feat", name = "feat: A new feature. Correlates with MINOR in SemVer"},
|
|
17
|
+
# {value = "docs", name = "docs: Documentation only changes"},
|
|
18
|
+
# {value = "style", name = "style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)"},
|
|
19
|
+
# {value = "refactor", name = "refactor: A code change that neither fixes a bug nor adds a feature"},
|
|
20
|
+
# {value = "perf", name = "perf: A code change that improves performance"},
|
|
21
|
+
# {value = "test", name = "test: Adding missing or correcting existing tests"},
|
|
22
|
+
# {value = "build", name = "build: Changes that affect the build system or external dependencies (example scopes: pip, docker, npm)"},
|
|
23
|
+
# {value = "ci", name = "ci: Changes to CI configuration files and scripts (example scopes: GitLabCI)"},
|
|
24
|
+
# {value = "chore", name = "chore: Other changes that don't modify src or test files"},
|
|
25
|
+
# {value = "wip", name = "wip: Work in progress"}
|
|
26
|
+
# ]
|
|
27
|
+
# message = "Select the type of change you are committing"
|
afterpython/pcu.py
CHANGED
|
@@ -3,8 +3,7 @@ pcu = "pip check updates", similar to ncu (npm check updates in Node.js)
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
6
|
-
|
|
7
|
-
from typing import TYPE_CHECKING, NamedTuple, TypeAlias, TypedDict
|
|
6
|
+
from typing import TYPE_CHECKING, TypedDict, NamedTuple, TypeAlias
|
|
8
7
|
|
|
9
8
|
if TYPE_CHECKING:
|
|
10
9
|
from tomlkit.toml_document import TOMLDocument
|
|
@@ -61,7 +60,6 @@ async def get_latest_versions(
|
|
|
61
60
|
) -> dict[str, Version | None]:
|
|
62
61
|
"""Get latest versions for a list of dependencies from PyPI."""
|
|
63
62
|
import httpx
|
|
64
|
-
|
|
65
63
|
from afterpython.utils import fetch_pypi_json
|
|
66
64
|
|
|
67
65
|
async def fetch_version(
|
|
@@ -35,7 +35,7 @@ jobs:
|
|
|
35
35
|
runs-on: ubuntu-latest
|
|
36
36
|
steps:
|
|
37
37
|
- name: Checkout code
|
|
38
|
-
uses: actions/checkout@
|
|
38
|
+
uses: actions/checkout@v4
|
|
39
39
|
|
|
40
40
|
- name: Install pnpm
|
|
41
41
|
uses: pnpm/action-setup@v4
|
|
@@ -43,19 +43,19 @@ jobs:
|
|
|
43
43
|
version: 10
|
|
44
44
|
|
|
45
45
|
- name: Set up Node.js
|
|
46
|
-
uses: actions/setup-node@
|
|
46
|
+
uses: actions/setup-node@v4
|
|
47
47
|
with:
|
|
48
48
|
node-version: '24'
|
|
49
49
|
cache: 'pnpm'
|
|
50
50
|
cache-dependency-path: './afterpython/_website/pnpm-lock.yaml'
|
|
51
51
|
|
|
52
52
|
- name: Set up Python
|
|
53
|
-
uses: actions/setup-python@
|
|
53
|
+
uses: actions/setup-python@v5
|
|
54
54
|
with:
|
|
55
55
|
python-version: '3.11'
|
|
56
56
|
|
|
57
57
|
- name: Install uv
|
|
58
|
-
uses: astral-sh/setup-uv@
|
|
58
|
+
uses: astral-sh/setup-uv@v5
|
|
59
59
|
with:
|
|
60
60
|
version: "latest"
|
|
61
61
|
|
|
@@ -69,9 +69,11 @@ jobs:
|
|
|
69
69
|
- name: Build website
|
|
70
70
|
run: |
|
|
71
71
|
ap build
|
|
72
|
+
env:
|
|
73
|
+
BASE_PATH: '/${{ github.event.repository.name }}'
|
|
72
74
|
|
|
73
75
|
- name: Upload artifact
|
|
74
|
-
uses: actions/upload-pages-artifact@
|
|
76
|
+
uses: actions/upload-pages-artifact@v3
|
|
75
77
|
with:
|
|
76
78
|
path: './afterpython/_website/build'
|
|
77
79
|
|
|
@@ -14,15 +14,15 @@ jobs:
|
|
|
14
14
|
|
|
15
15
|
steps:
|
|
16
16
|
- name: Checkout code
|
|
17
|
-
uses: actions/checkout@
|
|
17
|
+
uses: actions/checkout@v4
|
|
18
18
|
|
|
19
19
|
- name: Set up Python
|
|
20
|
-
uses: actions/setup-python@
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
21
|
with:
|
|
22
22
|
python-version: '3.11'
|
|
23
23
|
|
|
24
24
|
- name: Install uv
|
|
25
|
-
uses: astral-sh/setup-uv@
|
|
25
|
+
uses: astral-sh/setup-uv@v5
|
|
26
26
|
with:
|
|
27
27
|
version: "latest"
|
|
28
28
|
|
|
@@ -18,7 +18,7 @@ ignore = [
|
|
|
18
18
|
]
|
|
19
19
|
# extended rules:
|
|
20
20
|
extend-select = [
|
|
21
|
-
"I", # isort - import sorting
|
|
21
|
+
# "I", # isort - import sorting
|
|
22
22
|
"UP", # pyupgrade - upgrade syntax for newer Python
|
|
23
23
|
"B", # flake8-bugbear - find likely bugs
|
|
24
24
|
"SIM", # flake8-simplify - simplify code
|
|
@@ -1,71 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
|
|
3
2
|
from typing import TYPE_CHECKING
|
|
4
3
|
|
|
5
4
|
if TYPE_CHECKING:
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
|
|
8
5
|
from tomlkit.toml_document import TOMLDocument
|
|
9
6
|
|
|
10
|
-
from afterpython._typing import tContentType
|
|
11
|
-
|
|
12
7
|
import tomlkit
|
|
13
8
|
|
|
14
9
|
import afterpython as ap
|
|
15
|
-
from afterpython._io.toml import
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def get_default_thumbnail(
|
|
19
|
-
content_type: tContentType, file_path: Path | None = None
|
|
20
|
-
) -> str:
|
|
21
|
-
"""Get the default thumbnail value for a content type.
|
|
22
|
-
|
|
23
|
-
Reads default thumbnail configuration from afterpython.toml:
|
|
24
|
-
- Content type specific thumbnail: [website.{content_type}.thumbnail]
|
|
25
|
-
- Website-wide default thumbnail: [website.thumbnail]
|
|
26
|
-
- Falls back to empty string if no default is configured
|
|
27
|
-
|
|
28
|
-
Args:
|
|
29
|
-
content_type: The content type (blog, tutorial, etc.)
|
|
30
|
-
file_path: Optional path to the file needing the thumbnail (for calculating relative path)
|
|
31
|
-
|
|
32
|
-
Returns:
|
|
33
|
-
The thumbnail value to use (e.g., "./static/image.png", "../static/image.png", or "")
|
|
34
|
-
"""
|
|
35
|
-
from afterpython._io.toml import _from_tomlkit
|
|
36
|
-
from afterpython.utils import get_relative_static_prefix, normalize_static_path
|
|
37
|
-
|
|
38
|
-
afterpython = read_afterpython()
|
|
39
|
-
try:
|
|
40
|
-
# website_default_thumbnail = normalize_static_path(
|
|
41
|
-
# str(_from_tomlkit(afterpython.get("website", {})).get("thumbnail", ""))
|
|
42
|
-
# )
|
|
43
|
-
content_type_default_thumbnail = normalize_static_path(
|
|
44
|
-
str(
|
|
45
|
-
_from_tomlkit(afterpython.get("website", {}))
|
|
46
|
-
.get(content_type, {})
|
|
47
|
-
.get("thumbnail", "")
|
|
48
|
-
)
|
|
49
|
-
)
|
|
50
|
-
except ValueError as e:
|
|
51
|
-
print(f"Error in reading afterpython.toml for {content_type=}: {e}")
|
|
52
|
-
return "null"
|
|
53
|
-
|
|
54
|
-
# Determine the static prefix based on file location
|
|
55
|
-
if file_path is not None:
|
|
56
|
-
content_type_path = ap.paths.afterpython_path / content_type.lower()
|
|
57
|
-
static_prefix = get_relative_static_prefix(file_path, content_type_path)
|
|
58
|
-
else:
|
|
59
|
-
static_prefix = "./" # Default fallback
|
|
60
|
-
|
|
61
|
-
if content_type_default_thumbnail:
|
|
62
|
-
return f"{static_prefix}static{content_type_default_thumbnail}"
|
|
63
|
-
# use "thumbnail" set in afterpython.toml [website]
|
|
64
|
-
# elif website_default_thumbnail:
|
|
65
|
-
# Website-wide static is one level up from content_type folder
|
|
66
|
-
# return f"{static_prefix}../static{website_default_thumbnail}"
|
|
67
|
-
else:
|
|
68
|
-
return ""
|
|
10
|
+
from afterpython._io.toml import read_toml, write_toml, _to_tomlkit
|
|
69
11
|
|
|
70
12
|
|
|
71
13
|
def read_afterpython() -> TOMLDocument:
|
|
@@ -112,17 +54,12 @@ def init_afterpython():
|
|
|
112
54
|
return
|
|
113
55
|
afterpython_toml_path.touch()
|
|
114
56
|
print(f"Created {afterpython_toml_path}")
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
"
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
"url": "",
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
"logo_dark": "logo.svg",
|
|
125
|
-
"thumbnail": "thumbnail.png",
|
|
126
|
-
},
|
|
127
|
-
}
|
|
128
|
-
update_afterpython(default_data)
|
|
57
|
+
update_afterpython(
|
|
58
|
+
{
|
|
59
|
+
"company": {
|
|
60
|
+
"name": "",
|
|
61
|
+
"url": "",
|
|
62
|
+
},
|
|
63
|
+
"website": {"url": ""},
|
|
64
|
+
}
|
|
65
|
+
)
|
afterpython/tools/_git.py
CHANGED
|
@@ -58,7 +58,7 @@ def get_github_url() -> str | None:
|
|
|
58
58
|
return None
|
|
59
59
|
|
|
60
60
|
|
|
61
|
-
def
|
|
61
|
+
def setup_github_auth():
|
|
62
62
|
"""Guide user through GitHub authentication."""
|
|
63
63
|
if not has_gh():
|
|
64
64
|
print("""
|
|
@@ -89,6 +89,7 @@ Please authenticate with GitHub:
|
|
|
89
89
|
""")
|
|
90
90
|
return False
|
|
91
91
|
|
|
92
|
+
print("✓ GitHub authentication verified")
|
|
92
93
|
return True
|
|
93
94
|
|
|
94
95
|
|
|
@@ -106,32 +107,5 @@ def get_github_username() -> str | None:
|
|
|
106
107
|
return result.stdout.strip()
|
|
107
108
|
|
|
108
109
|
|
|
109
|
-
def get_current_user_id() -> int | None:
|
|
110
|
-
"""
|
|
111
|
-
Get the GitHub user ID of the currently authenticated user.
|
|
112
|
-
|
|
113
|
-
Returns:
|
|
114
|
-
User ID or None if error
|
|
115
|
-
"""
|
|
116
|
-
if not is_gh_authenticated():
|
|
117
|
-
return None
|
|
118
|
-
|
|
119
|
-
result = subprocess.run(
|
|
120
|
-
["gh", "api", "user", "--jq", ".id"],
|
|
121
|
-
capture_output=True,
|
|
122
|
-
text=True,
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
if result.returncode != 0:
|
|
126
|
-
print(f"Error fetching user ID: {result.stderr}")
|
|
127
|
-
return None
|
|
128
|
-
|
|
129
|
-
try:
|
|
130
|
-
return int(result.stdout.strip())
|
|
131
|
-
except ValueError:
|
|
132
|
-
print("Error: Failed to parse user ID")
|
|
133
|
-
return None
|
|
134
|
-
|
|
135
|
-
|
|
136
110
|
# TODO: use gh's token for pygithub to get repo issues
|
|
137
111
|
# def get_repo_issues(owner: str, repo: str) -> list[dict]:
|
afterpython/tools/commitizen.py
CHANGED
|
@@ -1,13 +1,53 @@
|
|
|
1
|
-
import shutil
|
|
2
|
-
|
|
3
1
|
import afterpython as ap
|
|
4
2
|
|
|
5
3
|
|
|
4
|
+
commitizen_default = {
|
|
5
|
+
"tool": {
|
|
6
|
+
"commitizen": {
|
|
7
|
+
"name": "cz_conventional_commits",
|
|
8
|
+
"tag_format": "v$version",
|
|
9
|
+
"version_scheme": "pep440",
|
|
10
|
+
"version_provider": "uv",
|
|
11
|
+
"update_changelog_on_bump": False,
|
|
12
|
+
"major_version_zero": True,
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
commitizen_pre_commit_hook = {
|
|
19
|
+
"repos": [
|
|
20
|
+
{
|
|
21
|
+
"repo": "https://github.com/commitizen-tools/commitizen",
|
|
22
|
+
"rev": "v4.10.0",
|
|
23
|
+
"hooks": [
|
|
24
|
+
{
|
|
25
|
+
"id": "commitizen",
|
|
26
|
+
"stages": ["commit-msg"],
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "commitizen-branch",
|
|
30
|
+
"stages": ["pre-push"],
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
6
38
|
def init_commitizen():
|
|
7
|
-
|
|
39
|
+
from afterpython.tools.pre_commit import update_pre_commit
|
|
40
|
+
from afterpython._io.toml import write_toml
|
|
41
|
+
|
|
42
|
+
afterpython_path = ap.paths.afterpython_path
|
|
43
|
+
commitizen_toml_path = afterpython_path / "cz.toml"
|
|
8
44
|
if commitizen_toml_path.exists():
|
|
9
45
|
print(f"Commitizen configuration file {commitizen_toml_path} already exists")
|
|
10
46
|
return
|
|
11
|
-
|
|
12
|
-
|
|
47
|
+
|
|
48
|
+
# Create commitizen configuration with default values
|
|
49
|
+
write_toml(commitizen_toml_path, commitizen_default)
|
|
13
50
|
print(f"Created {commitizen_toml_path}")
|
|
51
|
+
|
|
52
|
+
# add commitizen hook to .pre-commit-config.yaml
|
|
53
|
+
update_pre_commit(commitizen_pre_commit_hook)
|
|
@@ -1,48 +1,78 @@
|
|
|
1
1
|
import shutil
|
|
2
|
-
from pathlib import Path
|
|
3
2
|
|
|
4
3
|
import click
|
|
5
4
|
|
|
6
5
|
import afterpython as ap
|
|
7
6
|
|
|
8
7
|
|
|
9
|
-
def
|
|
10
|
-
"""
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
def init_release_workflow():
|
|
9
|
+
"""Initialize GitHub Actions release workflow.
|
|
10
|
+
|
|
11
|
+
Creates .github/workflows/release.yml in the user's project root.
|
|
12
|
+
This workflow triggers on version tags and:
|
|
13
|
+
- Builds the package with uv
|
|
14
|
+
- Publishes to PyPI using trusted publishing
|
|
15
|
+
- Creates GitHub releases (with pre-release detection)
|
|
16
|
+
"""
|
|
17
|
+
user_path = ap.paths.user_path
|
|
18
|
+
workflow_dir = user_path / ".github" / "workflows"
|
|
19
|
+
workflow_path = workflow_dir / "release.yml"
|
|
20
|
+
|
|
21
|
+
if workflow_path.exists():
|
|
22
|
+
click.echo(f"GitHub Actions release workflow {workflow_path} already exists")
|
|
13
23
|
return
|
|
14
24
|
|
|
15
|
-
# Create
|
|
16
|
-
|
|
25
|
+
# Create .github/workflows directory if it doesn't exist
|
|
26
|
+
workflow_dir.mkdir(parents=True, exist_ok=True)
|
|
17
27
|
|
|
18
28
|
# Copy template from package
|
|
19
|
-
template_path = ap.paths.templates_path /
|
|
29
|
+
template_path = ap.paths.templates_path / "release-workflow-template.yml"
|
|
20
30
|
if not template_path.exists():
|
|
21
31
|
raise FileNotFoundError(
|
|
22
32
|
f"Template file not found: {template_path}\n"
|
|
23
33
|
"This might indicate a corrupted installation. Please reinstall afterpython."
|
|
24
34
|
)
|
|
25
35
|
|
|
26
|
-
shutil.copy(template_path,
|
|
27
|
-
|
|
36
|
+
shutil.copy(template_path, workflow_path)
|
|
37
|
+
click.echo(f"Created {workflow_path}")
|
|
38
|
+
click.echo(
|
|
39
|
+
"\n📋 Next steps to enable releases:\n"
|
|
40
|
+
" 1. Enable PyPI trusted publishing:\n"
|
|
41
|
+
" - Go to https://pypi.org/manage/account/publishing/\n"
|
|
42
|
+
" - Add your repository and workflow file: release.yml\n"
|
|
43
|
+
" 2. Push tags to trigger releases:\n"
|
|
44
|
+
" - Use 'ap bump --release' for stable releases (auto-pushes tag)\n"
|
|
45
|
+
" - Use 'ap bump --pre' then 'ap release' for pre-releases\n"
|
|
46
|
+
)
|
|
28
47
|
|
|
29
48
|
|
|
30
|
-
def
|
|
31
|
-
"""
|
|
32
|
-
if ".yml" in workflow_name:
|
|
33
|
-
workflow_name = workflow_name.replace(".yml", "")
|
|
49
|
+
def init_deploy_workflow():
|
|
50
|
+
"""Initialize GitHub Actions deployment workflow for GitHub Pages.
|
|
34
51
|
|
|
52
|
+
Creates .github/workflows/deploy.yml in the user's project root.
|
|
53
|
+
This workflow triggers on pushes to main branch and:
|
|
54
|
+
- Builds the project website with 'ap build'
|
|
55
|
+
- Deploys to GitHub Pages
|
|
56
|
+
- Allows manual deployment via workflow_dispatch
|
|
57
|
+
"""
|
|
35
58
|
user_path = ap.paths.user_path
|
|
36
|
-
|
|
37
|
-
|
|
59
|
+
workflow_dir = user_path / ".github" / "workflows"
|
|
60
|
+
workflow_path = workflow_dir / "deploy.yml"
|
|
38
61
|
|
|
39
|
-
|
|
62
|
+
if workflow_path.exists():
|
|
63
|
+
click.echo(f"GitHub Actions deploy workflow {workflow_path} already exists")
|
|
64
|
+
return
|
|
40
65
|
|
|
66
|
+
# Create .github/workflows directory if it doesn't exist
|
|
67
|
+
workflow_dir.mkdir(parents=True, exist_ok=True)
|
|
41
68
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
69
|
+
# Copy template from package
|
|
70
|
+
template_path = ap.paths.templates_path / "deploy-workflow-template.yml"
|
|
71
|
+
if not template_path.exists():
|
|
72
|
+
raise FileNotFoundError(
|
|
73
|
+
f"Template file not found: {template_path}\n"
|
|
74
|
+
"This might indicate a corrupted installation. Please reinstall afterpython."
|
|
75
|
+
)
|
|
47
76
|
|
|
48
|
-
|
|
77
|
+
shutil.copy(template_path, workflow_path)
|
|
78
|
+
click.echo(f"Created {workflow_path}")
|