Skip to content

Commit 4e821d5

Browse files
committed
fix(post-gen-hook): populate project.yml with correct commit hash and paths
1 parent a4709d5 commit 4e821d5

1 file changed

Lines changed: 54 additions & 30 deletions

File tree

hooks/post_gen_project.py

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,11 @@
99
import pprint
1010
import subprocess
1111
import sys
12-
13-
# Used indirectly in the below Jinja2 block
14-
from collections import OrderedDict # pylint: disable=unused-import
12+
from collections import OrderedDict
1513
from logging import basicConfig, getLogger
1614
from pathlib import Path
1715

18-
import git
1916
import yaml
20-
from cookiecutter.repository import expand_abbreviations
2117

2218
LOG_FORMAT = json.dumps(
2319
{
@@ -35,38 +31,32 @@
3531

3632
def get_context() -> dict:
3733
"""Return the context as a dict"""
34+
# Import git-related modules here so they're only loaded when needed
35+
import git
36+
from cookiecutter.repository import expand_abbreviations
37+
3838
cookiecutter = None
3939
timestamp = datetime.datetime.now(datetime.UTC).isoformat(timespec="seconds")
4040

4141
##############
4242
# This section leverages cookiecutter's jinja interpolation
43-
# pylint: disable-next=unhashable-member
4443
cookiecutter_context_ordered: OrderedDict[str, str] = {{cookiecutter | pprint}} # type: ignore
4544
cookiecutter_context: dict[str, str] = dict(cookiecutter_context_ordered)
46-
47-
project_name = cookiecutter_context["project_slug"] # pylint: disable=unsubscriptable-object
48-
project_description = cookiecutter_context["project_short_description"] # pylint: disable=unsubscriptable-object
49-
template = cookiecutter_context["_template"] # pylint: disable=unsubscriptable-object
50-
output = cookiecutter_context["_output_dir"] # pylint: disable=unsubscriptable-object
5145
##############
5246

53-
try:
54-
if Path(template).is_absolute():
55-
template_path: Path = Path(template).resolve()
56-
else:
57-
output_path: Path = Path(output).resolve()
58-
template_path: Path = output_path.joinpath(template)
59-
60-
# IMPORTANT: If the specified template is remote (http/git/ssh) this SHOULD raise an exception. The remote logic is in the except block
61-
repo: git.Repo = git.Repo(template_path)
47+
project_name = cookiecutter_context["project_slug"]
48+
project_description = cookiecutter_context["project_short_description"]
49+
template = cookiecutter_context["_template"]
50+
output = cookiecutter_context["_output_dir"]
51+
# Get the branch specified via --checkout, but fall back to main
52+
branch = cookiecutter_context.get("_checkout") or "main"
6253

63-
# Expect this is a local template
64-
branch: str = str(repo.active_branch)
65-
dirty: bool = repo.is_dirty(untracked_files=True)
66-
template_commit_hash = git.cmd.Git().ls_remote(template_path, "HEAD")[:40]
67-
except (git.exc.InvalidGitRepositoryError, git.exc.NoSuchPathError):
68-
# This exception handling occurs every time the template repo is remote
54+
# Check if template is a remote URL or abbreviation
55+
is_remote_template = any(
56+
template.startswith(prefix) for prefix in ["http://", "https://", "git@", "gh:", "gl:", "bb:"]
57+
)
6958

59+
if is_remote_template:
7060
# From https://github.com/cookiecutter/cookiecutter/blob/b4451231809fb9e4fc2a1e95d433cb030e4b9e06/cookiecutter/config.py#L22
7161
abbreviations: dict[str, str] = {
7262
"gh": "https://github.com/{0}.git",
@@ -75,11 +65,36 @@ def get_context() -> dict:
7565
}
7666
template_repo: str = expand_abbreviations(template, abbreviations)
7767

78-
# This currently assumes main until https://github.com/cookiecutter/cookiecutter/issues/1759 is resolved
79-
branch: str = "main"
8068
dirty: bool = False
8169

70+
# For remote templates, get the commit hash from the remote
8271
template_commit_hash = git.cmd.Git().ls_remote(template_repo, branch)[:40]
72+
# Store the expanded URL
73+
template_location = template_repo
74+
else:
75+
# This is a local template path
76+
if Path(template).is_absolute():
77+
template_path: Path = Path(template).resolve()
78+
else:
79+
output_path: Path = Path(output).resolve()
80+
template_path: Path = output_path.joinpath(template).resolve()
81+
82+
try:
83+
repo: git.Repo = git.Repo(template_path)
84+
85+
# Get info from the local repository
86+
branch: str = str(repo.active_branch)
87+
dirty: bool = repo.is_dirty(untracked_files=True)
88+
# Get the actual commit hash from the local repository
89+
template_commit_hash = repo.head.commit.hexsha
90+
# Store the fully qualified template path for local templates
91+
template_location = str(template_path)
92+
except (git.exc.InvalidGitRepositoryError, git.exc.NoSuchPathError):
93+
# Not a git repository, fall back to empty values
94+
branch = "unknown"
95+
dirty = False
96+
template_commit_hash = ""
97+
template_location = str(template_path)
8398

8499
context: dict[str, str | dict[str, str | bool | dict[str, str | bool | dict[str, str]]]] = {}
85100
context["name"] = project_name
@@ -91,12 +106,21 @@ def get_context() -> dict:
91106
context["origin"]["template"]["branch"] = branch
92107
context["origin"]["template"]["commit hash"] = template_commit_hash
93108
context["origin"]["template"]["dirty"] = dirty
94-
context["origin"]["template"]["location"] = template
109+
context["origin"]["template"]["location"] = template_location
95110
context["origin"]["template"]["cookiecutter"] = {}
96111
context["origin"]["template"]["cookiecutter"] = cookiecutter_context
97112

98113
# Filter out unwanted cookiecutter context
99-
del cookiecutter_context["_output_dir"] # pylint: disable=unsubscriptable-object
114+
del cookiecutter_context["_output_dir"]
115+
116+
# Replace relative paths with fully qualified paths in cookiecutter context
117+
if "_template" in cookiecutter_context:
118+
cookiecutter_context["_template"] = template_location
119+
120+
if "_repo_dir" in cookiecutter_context:
121+
# For local templates, _repo_dir should be the same as the resolved template path
122+
# For remote templates, it would be the cloned directory, but we'll use the template location
123+
cookiecutter_context["_repo_dir"] = template_location
100124

101125
return context
102126

0 commit comments

Comments
 (0)