afterpython 0.1.0__py3-none-any.whl → 0.1.0.dev4__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.
Files changed (55) hide show
  1. afterpython/{templates/pre-commit-config-template.yaml → .pre-commit-config.yaml} +1 -1
  2. afterpython/_io/toml.py +3 -5
  3. afterpython/_paths.py +1 -1
  4. afterpython/_typing.py +2 -1
  5. afterpython/builders/__init__.py +3 -12
  6. afterpython/builders/metadata.py +3 -7
  7. afterpython/builders/molab.py +99 -0
  8. afterpython/cli/__init__.py +1 -0
  9. afterpython/cli/commands/build.py +63 -136
  10. afterpython/cli/commands/bump.py +0 -30
  11. afterpython/cli/commands/check.py +1 -1
  12. afterpython/cli/commands/clean.py +3 -4
  13. afterpython/cli/commands/commit.py +3 -21
  14. afterpython/cli/commands/commitizen.py +1 -1
  15. afterpython/cli/commands/dev.py +13 -80
  16. afterpython/cli/commands/format.py +1 -1
  17. afterpython/cli/commands/init.py +39 -48
  18. afterpython/cli/commands/install.py +2 -8
  19. afterpython/cli/commands/pre_commit.py +1 -1
  20. afterpython/cli/commands/preview.py +1 -1
  21. afterpython/cli/commands/start.py +4 -8
  22. afterpython/cli/commands/sync.py +21 -65
  23. afterpython/cli/commands/update.py +8 -31
  24. afterpython/cli/main.py +14 -75
  25. afterpython/const.py +2 -1
  26. afterpython/cz.toml +27 -0
  27. afterpython/pcu.py +1 -3
  28. afterpython/templates/deploy-workflow-template.yml +5 -22
  29. afterpython/templates/release-workflow-template.yml +3 -3
  30. afterpython/templates/ruff-template.toml +1 -1
  31. afterpython/tools/_afterpython.py +10 -73
  32. afterpython/tools/_git.py +2 -28
  33. afterpython/tools/commitizen.py +45 -5
  34. afterpython/tools/github_actions.py +53 -23
  35. afterpython/tools/myst.py +38 -82
  36. afterpython/tools/pre_commit.py +31 -6
  37. afterpython/tools/pyproject.py +2 -40
  38. afterpython/utils.py +3 -87
  39. {afterpython-0.1.0.dist-info → afterpython-0.1.0.dev4.dist-info}/METADATA +13 -39
  40. afterpython-0.1.0.dev4.dist-info/RECORD +47 -0
  41. {afterpython-0.1.0.dist-info → afterpython-0.1.0.dev4.dist-info}/WHEEL +1 -1
  42. afterpython/builders/content_json.py +0 -131
  43. afterpython/builders/index_md.py +0 -94
  44. afterpython/builders/jupyter_notebook.py +0 -293
  45. afterpython/builders/markdown.py +0 -271
  46. afterpython/cli/commands/add.py +0 -67
  47. afterpython/cli/commands/init_branch_rules.py +0 -9
  48. afterpython/cli/commands/lock.py +0 -23
  49. afterpython/cli/commands/remove.py +0 -67
  50. afterpython/templates/ci-workflow-template.yml +0 -197
  51. afterpython/templates/cz-template.toml +0 -7
  52. afterpython/templates/dependabot-template.yml +0 -30
  53. afterpython/tools/branch_rules.py +0 -106
  54. afterpython-0.1.0.dist-info/RECORD +0 -57
  55. {afterpython-0.1.0.dist-info → afterpython-0.1.0.dev4.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.add import add
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.format import format
20
- from afterpython.cli.commands.init import init
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.lock import lock
24
- from afterpython.cli.commands.pre_commit import pre_commit
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(cls=AliasGroup, context_settings={"help_option_names": ["-h", "--help"]})
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
@@ -1,4 +1,5 @@
1
1
  from afterpython._typing import tContentType
2
2
 
3
+
3
4
  NODEENV_VERSION = "24.11.0"
4
- CONTENT_TYPES: set[tContentType] = {"doc", "blog", "tutorial", "example", "guide"}
5
+ CONTENT_TYPES: list[tContentType] = ["doc", "blog", "tutorial", "example", "guide"]
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,43 +35,26 @@ jobs:
35
35
  runs-on: ubuntu-latest
36
36
  steps:
37
37
  - name: Checkout code
38
- uses: actions/checkout@v6
39
-
40
- - name: Install pnpm
41
- uses: pnpm/action-setup@v4
42
- with:
43
- version: 10
44
-
45
- - name: Set up Node.js
46
- uses: actions/setup-node@v6
47
- with:
48
- node-version: '24'
49
- cache: 'pnpm'
50
- cache-dependency-path: './afterpython/_website/pnpm-lock.yaml'
38
+ uses: actions/checkout@v4
51
39
 
52
40
  - name: Set up Python
53
- uses: actions/setup-python@v6
41
+ uses: actions/setup-python@v5
54
42
  with:
55
43
  python-version: '3.11'
56
44
 
57
45
  - name: Install uv
58
- uses: astral-sh/setup-uv@v7
46
+ uses: astral-sh/setup-uv@v5
59
47
  with:
60
48
  version: "latest"
61
49
 
62
50
  - name: Install afterpython
63
51
  run: uv pip install --system afterpython
64
52
 
65
- - name: Install website dependencies
66
- run: pnpm install
67
- working-directory: ./afterpython/_website
68
-
69
53
  - name: Build website
70
- run: |
71
- ap build
54
+ run: ap build
72
55
 
73
56
  - name: Upload artifact
74
- uses: actions/upload-pages-artifact@v4
57
+ uses: actions/upload-pages-artifact@v3
75
58
  with:
76
59
  path: './afterpython/_website/build'
77
60
 
@@ -14,15 +14,15 @@ jobs:
14
14
 
15
15
  steps:
16
16
  - name: Checkout code
17
- uses: actions/checkout@v6
17
+ uses: actions/checkout@v4
18
18
 
19
19
  - name: Set up Python
20
- uses: actions/setup-python@v6
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@v7
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 _to_tomlkit, read_toml, write_toml
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
- default_data = {
116
- "company": {
117
- "name": "",
118
- "url": "",
119
- },
120
- "website": {
121
- "url": "",
122
- "favicon": "favicon.svg",
123
- "logo": "logo.svg",
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 is_gh_authenticated():
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]:
@@ -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
- commitizen_toml_path = ap.paths.afterpython_path / "cz.toml"
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
- cz_template_path = ap.paths.templates_path / "cz-template.toml"
12
- shutil.copy(cz_template_path, commitizen_toml_path)
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 _copy_github_template(template_name: str, target_path: Path):
10
- """Helper to copy GitHub-related templates"""
11
- if target_path.exists():
12
- click.echo(f"{target_path} already exists")
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 parent directory if it doesn't exist
16
- target_path.parent.mkdir(parents=True, exist_ok=True)
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 / template_name
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, target_path)
27
- print(f"Created {target_path}")
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 create_workflow(workflow_name: str):
31
- """Create a GitHub Actions workflow from template"""
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
- workflow_path = user_path / ".github" / "workflows" / f"{workflow_name}.yml"
37
- template_name = f"{workflow_name}-workflow-template.yml"
59
+ workflow_dir = user_path / ".github" / "workflows"
60
+ workflow_path = workflow_dir / "deploy.yml"
38
61
 
39
- _copy_github_template(template_name, workflow_path)
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
- def create_dependabot():
43
- """Create Dependabot configuration for GitHub Actions updates"""
44
- user_path = ap.paths.user_path
45
- dependabot_path = user_path / ".github" / "dependabot.yml"
46
- template_name = "dependabot-template.yml"
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
- _copy_github_template(template_name, dependabot_path)
77
+ shutil.copy(template_path, workflow_path)
78
+ click.echo(f"Created {workflow_path}")