Skip to content

Commit 426a90a

Browse files
committed
feat: add a ton of half finished logic for release handling that will be refactored soon
1 parent 18a10d8 commit 426a90a

File tree

6 files changed

+117
-24
lines changed

6 files changed

+117
-24
lines changed

.env

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,22 @@
22
# Users can override these by creating a .env.local file (not committed to git)
33

44
# App author name used for cache directory paths
5-
COOKIECUTTER_ROBUST_PYTHON_APP_AUTHOR=robust-python
5+
COOKIECUTTER_ROBUST_PYTHON__DEMOS_CACHE_FOLDER=""
66

7-
# Main branch name (typically used for stable releases)
8-
COOKIECUTTER_ROBUST_PYTHON_MAIN_BRANCH=main
7+
COOKIECUTTER_ROBUST_PYTHON__APP_NAME="cookiecutter-robust-python"
8+
COOKIECUTTER_ROBUST_PYTHON__APP_AUTHOR="robust-python"
9+
COOKIECUTTER_ROBUST_PYTHON__REMOTE="origin"
10+
COOKIECUTTER_ROBUST_PYTHON__MAIN_BRANCH="main"
11+
COOKIECUTTER_ROBUST_PYTHON__DEVELOP_BRANCH="develop"
912

10-
# Development branch name (where feature development occurs)
11-
COOKIECUTTER_ROBUST_PYTHON_DEVELOP_BRANCH=develop
13+
ROBUST_PYTHON_DEMO__APP_NAME="robust-python-demo"
14+
ROBUST_PYTHON_DEMO__APP_AUTHOR="robust-python"
15+
ROBUST_PYTHON_DEMO__REMOTE="origin"
16+
ROBUST_PYTHON_DEMO__MAIN_BRANCH="main"
17+
ROBUST_PYTHON_DEMO__DEVELOP_BRANCH="develop"
18+
19+
ROBUST_MATURIN_DEMO__APP_NAME="robust-maturin-demo"
20+
ROBUST_MATURIN_DEMO__APP_AUTHOR="robust-python"
21+
ROBUST_MATURIN_DEMO__REMOTE="origin"
22+
ROBUST_MATURIN_DEMO__MAIN_BRANCH="main"
23+
ROBUST_MATURIN_DEMO__DEVELOP_BRANCH="develop"

noxfile.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import os
88
import shutil
9+
from dataclasses import dataclass
910
from pathlib import Path
1011

1112
import nox
@@ -66,6 +67,25 @@
6667
)
6768

6869

70+
@dataclass
71+
class RepoMetadata:
72+
"""Metadata for a given repo."""
73+
app_name: str
74+
app_author: str
75+
remote: str
76+
main_branch: str
77+
develop_branch: str
78+
79+
80+
TEMPLATE: RepoMetadata = RepoMetadata(
81+
app_name=os.getenv("COOKIECUTTER_ROBUST_PYTHON__APP_NAME"),
82+
app_author=os.getenv("COOKIECUTTER_ROBUST_PYTHON__APP_AUTHOR"),
83+
remote=os.getenv("COOKIECUTTER_ROBUST_PYTHON__REMOTE"),
84+
main_branch=os.getenv("COOKIECUTTER_ROBUST_PYTHON__MAIN_BRANCH"),
85+
develop_branch=os.getenv("COOKIECUTTER_ROBUST_PYTHON__DEVELOP_BRANCH")
86+
)
87+
88+
6989
@nox.session(python=DEFAULT_TEMPLATE_PYTHON_VERSION, name="generate-demo")
7090
def generate_demo(session: Session) -> None:
7191
"""Generates a project demo using the cookiecutter-robust-python template."""
@@ -103,6 +123,7 @@ def lint_from_demo(session: Session):
103123
session.log("Installing linting dependencies for the generated project...")
104124
session.install("-e", ".", "--group", "dev", "--group", "lint")
105125
session.run("python", LINT_FROM_DEMO_SCRIPT, *LINT_FROM_DEMO_OPTIONS, *session.posargs)
126+
session.install_and_run_script()
106127

107128

108129
@nox.session(python=DEFAULT_TEMPLATE_PYTHON_VERSION)

scripts/release-demo.py

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,30 @@
11
# /// script
22
# requires-python = ">=3.10"
33
# dependencies = [
4-
# cookiecutter,
5-
# cruft,
6-
# typer,
4+
# "cookiecutter",
5+
# "cruft",
6+
# "python-dotenv",
7+
# "typer",
78
# ]
89
# ///
10+
import itertools
11+
import subprocess
912
from pathlib import Path
1013
from typing import Annotated
1114

1215
import typer
1316
from cookiecutter.utils import work_in
17+
from loguru import logger
1418

19+
from util import get_current_branch
1520
from util import get_demo_name
21+
from util import gh
1622
from util import git
17-
from util import is_ancestor
18-
from util import uv
19-
from util import validate_is_synced_ancestor
23+
from util import nox
24+
from util import require_clean_and_up_to_date_repo
2025
from util import FolderOption
26+
from util import RepoMetadata
27+
from util import DEMO
2128

2229

2330
cli: typer.Typer = typer.Typer()
@@ -26,25 +33,51 @@
2633
@cli.callback(invoke_without_command=True)
2734
def release_demo(
2835
demos_cache_folder: Annotated[Path, FolderOption("--demos-cache-folder", "-c")],
29-
min_python_version: Annotated[str, typer.Option("--min-python-version")],
30-
max_python_version: Annotated[str, typer.Option("--max-python-version")],
3136
add_rust_extension: Annotated[bool, typer.Option("--add-rust-extension", "-r")] = False
3237
) -> None:
3338
"""Creates a release of the demo's current develop branch if changes exist."""
3439
demo_name: str = get_demo_name(add_rust_extension=add_rust_extension)
3540
demo_path: Path = demos_cache_folder / demo_name
3641

42+
with work_in(demo_path):
43+
require_clean_and_up_to_date_repo()
44+
git("checkout", DEMO.develop_branch)
45+
try:
46+
nox("setup-release", "--", "MINOR")
47+
logger.success(f"Successfully created release {demo_name}")
48+
_ensure_github_repo_set(repo=DEMO)
3749

50+
except subprocess.CalledProcessError as error:
51+
logger.warning(f"Failed to setup release: {error}")
52+
_rollback_failed_release()
53+
raise error
3854

55+
git("push", "-u")
56+
_create_demo_pr(version=)
3957

40-
def _validate_demo_develop_up_to_date(demo_path: Path) -> None:
41-
"""Ensures the demo's develop branch is up to date."""
42-
with work_in(demo_path):
43-
validate_is_synced_ancestor(ancestor=)
58+
59+
def _ensure_github_repo_set(repo: RepoMetadata) -> None:
60+
"""Ensures the repo has a github repo set."""
61+
gh("repo", "set-default", repo.app_author, repo.app_name)
4462

4563

64+
def _rollback_failed_release() -> None:
65+
"""Returns the demo repo back to the state it was prior to the release attempt."""
66+
starting_demo_branch: str = get_current_branch()
4667

68+
if starting_demo_branch != "develop":
69+
git("checkout", DEMO.develop_branch)
4770

71+
git("checkout", ".")
72+
git("branch", "-D", starting_demo_branch)
4873

4974

75+
def _create_demo_pr(version: str) -> None:
76+
"""Creates a pull request to merge the demo's feature branch into ."""
77+
pr_kwargs: dict[str, str] = {
78+
"--title": f"Release/{version}"
79+
}
80+
publish_release_commands: list[list[str]] = [
81+
["gh", "pr", "create", *itertools.chain(pr_kwargs.items())],
82+
]
5083

scripts/util.py

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@
1616
import subprocess
1717
import sys
1818
from contextlib import contextmanager
19+
from dataclasses import dataclass
1920
from functools import partial
2021
from pathlib import Path
2122
from typing import Any
2223
from typing import Callable
2324
from typing import Generator
2425
from typing import Literal
2526
from typing import Optional
27+
from typing import TypedDict
2628
from typing import overload
2729

2830
import cruft
@@ -60,8 +62,31 @@ def _load_env() -> None:
6062
)
6163

6264

63-
MAIN_BRANCH: str = os.getenv("COOKIECUTTER_ROBUST_PYTHON_MAIN_BRANCH", "main")
64-
DEVELOP_BRANCH: str = os.getenv("COOKIECUTTER_ROBUST_PYTHON_DEVELOP_BRANCH", "develop")
65+
@dataclass
66+
class RepoMetadata:
67+
"""Metadata for a given repo."""
68+
app_name: str
69+
app_author: str
70+
remote: str
71+
main_branch: str
72+
develop_branch: str
73+
74+
75+
TEMPLATE: RepoMetadata = RepoMetadata(
76+
app_name=os.getenv("COOKIECUTTER_ROBUST_PYTHON__APP_NAME"),
77+
app_author=os.getenv("COOKIECUTTER_ROBUST_PYTHON__APP_AUTHOR"),
78+
remote=os.getenv("COOKIECUTTER_ROBUST_PYTHON__REMOTE"),
79+
main_branch=os.getenv("COOKIECUTTER_ROBUST_PYTHON__MAIN_BRANCH"),
80+
develop_branch=os.getenv("COOKIECUTTER_ROBUST_PYTHON__DEVELOP_BRANCH")
81+
)
82+
83+
DEMO: RepoMetadata = RepoMetadata(
84+
app_name=os.getenv("ROBUST_DEMO__APP_NAME"),
85+
app_author=os.getenv("ROBUST_DEMO__APP_AUTHOR"),
86+
remote=os.getenv("ROBUST_DEMO__REMOTE"),
87+
main_branch=os.getenv("ROBUST_DEMO__MAIN_BRANCH"),
88+
develop_branch=os.getenv("ROBUST_DEMO__DEVELOP_BRANCH")
89+
)
6590

6691

6792
def remove_readonly(func: Callable[[str], Any], path: str, _: Any) -> None:
@@ -93,18 +118,20 @@ def run_command(command: str, *args: str, ignore_error: bool = False) -> Optiona
93118
return None
94119
print(error.stdout, end="")
95120
print(error.stderr, end="", file=sys.stderr)
96-
raise
121+
raise error
97122

98123

99124
git: partial[subprocess.CompletedProcess] = partial(run_command, "git")
100125
uv: partial[subprocess.CompletedProcess] = partial(run_command, "uv")
126+
nox: partial[subprocess.CompletedProcess] = partial(run_command, "nox")
127+
gh: partial[subprocess.CompletedProcess] = partial(run_command, "gh")
101128

102129

103130
def require_clean_and_up_to_date_repo() -> None:
104131
"""Checks if the repo is clean and up to date with any important branches."""
105132
git("fetch")
106133
git("status", "--porcelain")
107-
validate_is_synced_ancestor(ancestor=MAIN_BRANCH, descendent=DEVELOP_BRANCH)
134+
validate_is_synced_ancestor(ancestor=DEMO.main_branch, descendent=DEMO.develop_branch)
108135

109136

110137
def validate_is_synced_ancestor(ancestor: str, descendent: str) -> None:

{{cookiecutter.project_name}}/scripts/publish-release.py

Whitespace-only changes.

{{cookiecutter.project_name}}/scripts/setup-release.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
import subprocess
55
from typing import Optional
66

7-
from scripts.util import require_clean_and_up_to_date_repo
87
from util import REPO_FOLDER
98
from util import bump_version
109
from util import check_dependencies
1110
from util import create_release_branch
1211
from util import get_bumped_package_version
1312
from util import get_package_version
13+
from util import require_clean_and_up_to_date_repo
1414

1515

1616
def main() -> None:
@@ -21,9 +21,9 @@ def main() -> None:
2121

2222

2323
def get_parser() -> argparse.ArgumentParser:
24-
"""Creates the argument parser for prepare-release."""
24+
"""Creates the argument parser for setup-release."""
2525
parser: argparse.ArgumentParser = argparse.ArgumentParser(
26-
prog="prepare-release", usage="python ./scripts/prepare-release.py patch"
26+
prog="setup-release", usage="python ./scripts/setup-release.py patch"
2727
)
2828
parser.add_argument(
2929
"increment",

0 commit comments

Comments
 (0)