Skip to content
This repository was archived by the owner on Jul 29, 2024. It is now read-only.

Commit dbb9400

Browse files
Multiple small changes (#45)
2 parents 69f86e0 + aae9705 commit dbb9400

File tree

7 files changed

+83
-8
lines changed

7 files changed

+83
-8
lines changed

src/PythonProjectBootstrapper/ProjectGenerationUtils.py

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,13 @@ def CopyToOutputDir(
190190

191191
prompt_file = src_dir / prompt_filename
192192
if prompt_file.is_file():
193+
dest_filename = dest_dir / prompt_filename
194+
195+
if dest_filename.is_file():
196+
dest_filename.unlink()
197+
193198
shutil.move(prompt_file, dest_dir)
194-
PathEx.EnsureFile(dest_dir / prompt_filename)
199+
PathEx.EnsureFile(dest_filename)
195200

196201
# existing_manifest will be populated/updated as necessary and saved
197202
generated_manifest: dict[str, str] = CreateManifest(src_dir)
@@ -209,7 +214,7 @@ def CopyToOutputDir(
209214
with open(potential_manifest, "r") as existing_manifest_file:
210215
existing_manifest = yaml.load(existing_manifest_file, Loader=yaml.Loader)
211216

212-
# Removing <prompt_filename> from the manifest for backward compatability.
217+
# Removing <prompt_filename> from the manifest for backward compatibility.
213218
# Previous iterations of PythonProjectBootstrapper saved "<prompt_filename>"" in the manifest file when it should not have been there
214219
# (the manifest was created using the contents of the temporary directory and "<prompt_filename>"" was there but was removed from the output directory)
215220
# This results in "<prompt_filename>" being listed as a removed file since it exists in the manifest but not in the output directory
@@ -262,9 +267,31 @@ def CopyToOutputDir(
262267
and current_file_hash == existing_manifest[rel_filepath]
263268
):
264269
modified_template_files.append(output_dir_filepath.as_posix())
270+
elif potential_manifest.is_file():
271+
# If here, the file no longer exists. We still want the file to exist in the manifest
272+
# (so that future generations are still aware of it), but do not want it to be created
273+
# again.
274+
merged_manifest[rel_filepath] = generated_hash
275+
276+
while True:
277+
sys.stdout.write(
278+
f"\nWould you like to recreate {str(output_dir_filepath)}? [yes/no]: "
279+
)
280+
recreate = input().strip().lower()
281+
282+
if recreate in ["yes", "y"]:
283+
added_files.append(output_dir_filepath.as_posix())
284+
break
285+
286+
if recreate in ["no", "n"]:
287+
generated_filename = PathEx.EnsureFile(src_dir / rel_filepath)
288+
generated_filename.unlink()
289+
290+
break
265291
else:
266-
added_files.append(output_dir_filepath.as_posix())
292+
# If here, we are looking at a first time generation and don't need to prompt
267293
merged_manifest[rel_filepath] = generated_hash
294+
added_files.append(output_dir_filepath.as_posix())
268295

269296
# create and save manifest
270297
yaml_comments = textwrap.dedent(

src/PythonProjectBootstrapper/package/hooks/post_gen_project.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def SavePrompts() -> None:
101101
""",
102102
)
103103

104-
{% if cookiecutter.minisign_public_key != 'none' %}
104+
{% if cookiecutter.minisign_public_key.strip().lower() != 'none' %}
105105
prompts["Save the Minisign Private Key"] = textwrap.dedent(
106106
f"""\
107107
In this step, we will save the Minisign private key as a GitHub Action Secret.
@@ -186,6 +186,23 @@ def SavePrompts() -> None:
186186
""",
187187
)
188188

189+
{% if cookiecutter.minisign_public_key.strip().lower() != 'none' %}
190+
prompts["Validate Minisign Signature"] = textwrap.dedent(
191+
"""\
192+
In this step, we validate that a binary was correctly signed.
193+
194+
1. Download a binary and its corresponding signature file from {{ cookiecutter.github_url }}/{{ cookiecutter.github_username }}/{{ cookiecutter.github_project_name }}/releases/latest.
195+
2. In the download directory, run;
196+
197+
docker run -i --rm -v ".:/host" jedisct1/minisign -V -P {{ cookiecutter.minisign_public_key }} -m /host/<binary_name>
198+
199+
replacing '<binary_name>' with the name of the file downloaded in the previous step.
200+
3. Verify that 'Signature and comment signature verified' is displayed in the output.
201+
""",
202+
)
203+
204+
{% endif %}
205+
189206
{% if cookiecutter.openssf_best_practices_badge_id != "none" %}
190207
prompts["Update the OpenSSF Best Practices Badge [Basics]"] = textwrap.dedent(
191208
"""\

src/PythonProjectBootstrapper/package/hooks/startup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def Execute(
3333
step-by-step instructions on how to set up your project so that it works with 3rd party
3434
solutions (GitHub, PyPi, etc.).
3535
36-
The entire process should take about 20 minutes to complete.
36+
The entire process should take about 25 minutes to complete.
3737
""",
3838
)
3939

src/PythonProjectBootstrapper/package/{{ cookiecutter.__empty_dir }}/.github/workflows/standard.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,6 @@ jobs:
232232
release_sources_configuration_filename: .github/release_sources.yaml
233233
secrets:
234234
PYPI_TOKEN: {% raw %}${{ secrets.PYPI_TOKEN }}{% endraw %}
235-
{% if cookiecutter.minisign_public_key != 'none' %}
235+
{% if cookiecutter.minisign_public_key.strip().lower() != 'none' %}
236236
MINISIGN_PRIVATE_KEY: {% raw %}${{ secrets.MINISIGN_PRIVATE_KEY }}{% endraw %}
237237
{% endif %}

src/PythonProjectBootstrapper/package/{{ cookiecutter.__empty_dir }}/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,6 @@ Deactivate*.sh
1212
build/**
1313
dist/**
1414
src/{{ cookiecutter.pypi_project_name }}.egg-info/**
15+
16+
key.pri
17+
key.pub

src/PythonProjectBootstrapper/package/{{ cookiecutter.__empty_dir }}/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/{{ cookiecutter.pypi_project_name }}?color=dark-green)](https://pypi.org/project/{{ cookiecutter.pypi_project_name | pypi_string }}/)
99
[![PyPI - Version](https://img.shields.io/pypi/v/{{ cookiecutter.pypi_project_name }}?color=dark-green)](https://pypi.org/project/{{ cookiecutter.pypi_project_name | pypi_string }}/)
1010
[![PyPI - Downloads](https://img.shields.io/pypi/dm/{{ cookiecutter.pypi_project_name }})](https://pypistats.org/packages/{{ cookiecutter.pypi_project_name | pypi_string }})
11-
{% if cookiecutter.openssf_best_practices_badge_id != 'none' -%}
11+
{% if cookiecutter.openssf_best_practices_badge_id.strip().lower() != 'none' -%}
1212
[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/{{ cookiecutter.openssf_best_practices_badge_id }}/badge)](https://www.bestpractices.dev/projects/{{ cookiecutter.openssf_best_practices_badge_id }})
1313
{% endif %}
1414
<!-- END: Exclude Package -->
@@ -48,7 +48,7 @@ Download an executable for Linux, MacOS, or Windows to use the functionality pro
4848
1. Download the archive for the latest release [here]({{ cookiecutter.github_url }}/{{ cookiecutter.github_username }}/{{ cookiecutter.github_project_name }}/releases/latest); the files will begin with `exe.` and contain the name of your operating system.
4949
2. Decompress the archive
5050

51-
{% if cookiecutter.minisign_public_key != 'none' %}
51+
{% if cookiecutter.minisign_public_key.strip().lower() != 'none' %}
5252
#### Verifying Signed Executables
5353

5454
Executables are signed and validated using [Minisign](https://jedisct1.github.io/minisign/).

tests/ProjectGenerationUtils_UnitTest.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,34 @@ def test_CopyToOutputDir_overwritePrompt(fs, overwrite):
175175
assert status.st_mode & S_IRUSR == S_IRUSR
176176

177177

178+
# ----------------------------------------------------------------------
179+
@pytest.mark.parametrize("recreate", ["y", "n"])
180+
def test_CopyToOutputDir_recreatePrompt(fs, recreate):
181+
src = Path("src")
182+
src2 = Path("src2")
183+
dest = Path("dest")
184+
185+
files1 = [("testFile", "abc"), ("testFile2", "def"), ("testFile3", "hello")]
186+
files2 = [("testFile", "abc"), ("testFile2", "def"), ("testFile3", "hi")]
187+
188+
for filepath, content in files1:
189+
fs.create_file(src / filepath, contents=content)
190+
fs.create_file(src2 / filepath, contents=content)
191+
192+
fs.create_dir(dest)
193+
194+
CopyToOutputDir(src_dir=src, dest_dir=dest)
195+
196+
dest_filename = dest / "testFile"
197+
198+
dest_filename.unlink()
199+
200+
with patch("builtins.input", lambda *args: recreate):
201+
CopyToOutputDir(src_dir=src2, dest_dir=dest)
202+
203+
assert dest_filename.is_file() == (recreate == "y")
204+
205+
178206
# ----------------------------------------------------------------------
179207
def test_CopyToOutputDir_no_prompt(fs):
180208
# Test for the following:

0 commit comments

Comments
 (0)