diff --git a/.github/etc/dictionary.txt b/.github/etc/dictionary.txt index cad5dda..12daada 100644 --- a/.github/etc/dictionary.txt +++ b/.github/etc/dictionary.txt @@ -14,5 +14,6 @@ refurb skopeo syft taskfile +winget zenable zizmor diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e80f55a..0e30011 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -106,15 +106,10 @@ jobs: name: Windows Smoke Test runs-on: windows-latest steps: - # Note: no checkout step. The cookiecutter template directory contains - # characters (pipe, quotes) that are illegal on NTFS, so we cannot check - # out the repo on Windows. Instead, cookiecutter fetches the template - # directly from the remote branch. - name: Setup uv uses: astral-sh/setup-uv@v7 with: python-version: ${{ env.python_version }} - # No checkout in this job (NTFS-illegal chars in template dir), so disable cache enable-cache: false ignore-empty-workdir: true - name: Install Task @@ -126,24 +121,30 @@ jobs: env: RUN_POST_HOOK: 'true' SKIP_GIT_PUSH: 'true' - TEMPLATE_REF: ${{ github.event.pull_request.head.sha || github.sha }} + TEMPLATE_REPO: ${{ github.repository }} + TEMPLATE_REF: ${{ github.head_ref || github.ref_name }} run: | git config --global user.name "CI Automation" git config --global user.email "ci@zenable.io" - # The template directory name contains NTFS-illegal characters - # (double quotes, pipe). Use extract_template_zip.py to safely - # extract and rename only the top-level template dir. - zipUrl="https://github.com/${{ github.repository }}/archive/${TEMPLATE_REF}.zip" - scriptUrl="https://raw.githubusercontent.com/${{ github.repository }}/${TEMPLATE_REF}/scripts/extract_template_zip.py" - tmpdir=$(mktemp -d) - curl -fsSL "$zipUrl" -o "$tmpdir/template.zip" - curl -fsSL "$scriptUrl" -o "$tmpdir/extract_template_zip.py" - repoDir=$(python3 "$tmpdir/extract_template_zip.py" "$tmpdir/template.zip" "$tmpdir/src") - - uvx --with gitpython cookiecutter "$repoDir" --no-input \ - project_name="ci-test-project" \ - --output-dir "$RUNNER_TEMP" + # Use the same command as README.md (gh: syntax). On Windows, if the + # default branch still has NTFS-illegal paths (pre-merge), the gh: clone + # fails because cookiecutter clones main first. Fall back to a local + # clone in that case. After merge, the gh: command succeeds directly. + uvx --with gitpython cookiecutter \ + "gh:${TEMPLATE_REPO}" \ + --checkout "${TEMPLATE_REF}" \ + --no-input project_name="ci-test-project" \ + --output-dir "$RUNNER_TEMP" \ + || { + echo "::warning::gh: clone failed (expected pre-merge on Windows). Using local clone." + template_dir="$RUNNER_TEMP/template-repo" + git clone --no-checkout "https://github.com/${TEMPLATE_REPO}.git" "$template_dir" + git -C "$template_dir" checkout "${TEMPLATE_REF}" + uvx --with gitpython cookiecutter "$template_dir" \ + --no-input project_name="ci-test-project" \ + --output-dir "$RUNNER_TEMP" + } - name: Verify generated project shell: pwsh run: | diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a3e3cf0..896d7fc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,5 @@ --- -exclude: '^{{cookiecutter\.project_name\|replace\(" ", ""\)}}/.*' +exclude: '^{{cookiecutter\.project_name}}/.*' repos: - repo: https://github.com/astral-sh/ruff-pre-commit rev: 3db93a2be6f214ed722bf7bce095ec1b1715422a # frozen: v0.14.2 diff --git a/README.md b/README.md index 5b1008c..b1e1e32 100644 --- a/README.md +++ b/README.md @@ -24,14 +24,19 @@ For more details, see our [documentation](docs/index.md). ## Getting Started 1. Create an [empty GitHub repository](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-new-repository) -1. Ensure you have [`brew`](https://brew.sh/) installed -1. Generate a project with the same name as the repo you just made: +1. Install the prerequisites and generate your project: + + **macOS / Linux (Homebrew)** ```bash - # Install the prerequisites brew install uv go-task + uvx --with gitpython cookiecutter gh:zenable-io/ai-native-python + ``` + + **Windows (PowerShell)** - # Initialize your project + ```powershell + winget install astral-sh.uv Task.Task uvx --with gitpython cookiecutter gh:zenable-io/ai-native-python ``` diff --git a/Taskfile.yml b/Taskfile.yml index 6b4f016..59fa561 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -93,12 +93,12 @@ tasks: update: desc: Update the project dev and runtime dependencies cmds: - # This currently assumes uv was installed via uv (locally); we will want to make it more flexible in the future - - '{{if ne .GITHUB_ACTIONS "true"}}brew upgrade uv{{end}}' + # Upgrade uv via brew on macOS/Linux when running locally (not CI) + - '{{if and (ne .GITHUB_ACTIONS "true") (ne OS "windows")}}brew upgrade uv{{end}}' - uv tool upgrade --all - pre-commit autoupdate --freeze --jobs 4 # Copy the newly updated config into the project template, excluding the exclude line - - cat .pre-commit-config.yaml | grep -v ^exclude > '{{`{{cookiecutter.project_name|replace(" ", "")}}`}}/.pre-commit-config.yaml' + - cat .pre-commit-config.yaml | grep -v ^exclude > '{{`{{cookiecutter.project_name}}`}}/.pre-commit-config.yaml' - uv lock --upgrade # This can take a while but it's required for the following step to update BuildKit in the docker driver - '{{if eq .CLI_ARGS "all"}}docker buildx rm multiplatform || true{{end}}' diff --git a/docs/hooks.md b/docs/hooks.md index 9157a64..d98dfaa 100644 --- a/docs/hooks.md +++ b/docs/hooks.md @@ -46,7 +46,7 @@ task init # Installs pre-commit and sets up hooks, alongside setting up other p 6. **GitHub Actions** - Actionlint validation 7. **OpenAPI** - Schema validation -For the full list of hooks, see `{{cookiecutter.project_name|replace(" ", "")}}/.pre-commit-config.yaml` +For the full list of hooks, see `{{cookiecutter.project_name}}/.pre-commit-config.yaml` ### Configuration diff --git a/hooks/pre_gen_project.py b/hooks/pre_gen_project.py index 61d21bc..d9c7d1c 100755 --- a/hooks/pre_gen_project.py +++ b/hooks/pre_gen_project.py @@ -16,11 +16,16 @@ def validate_project_name() -> None: - """Validate that project_name starts with an alphabetical character.""" - # Check if project_name starts with an alphabetical character + """Validate that project_name starts with an alphabetical character and contains no spaces.""" if not re.match(r"^[a-zA-Z]", PROJECT_NAME): LOG.error( - "Invalid project name '%s': Python project names must start with an alphabetical character (a-z or A-Z).", + "Invalid project name '%s': must start with an alphabetical character (a-z or A-Z).", + PROJECT_NAME, + ) + sys.exit(1) + if " " in PROJECT_NAME: + LOG.error( + "Invalid project name '%s': must not contain spaces. Use hyphens instead (e.g. 'my-project').", PROJECT_NAME, ) sys.exit(1) diff --git a/scripts/extract_template_zip.py b/scripts/extract_template_zip.py deleted file mode 100755 index 88ec0fb..0000000 --- a/scripts/extract_template_zip.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python3 -"""Extract a cookiecutter template from a GitHub archive zip. - -The top-level template directory in the zip contains NTFS-illegal characters -(pipe, double-quotes) from the Jinja2 expression in -``{{cookiecutter.project_name|replace(" ", "")}}``. This script renames -**only** that top-level directory to the safe ``{{cookiecutter.project_name}}`` -form while preserving nested cookiecutter directories like -``{{cookiecutter.project_slug}}`` so cookiecutter renders them correctly. - -Usage: - python extract_template_zip.py - -Prints the path to the extracted template root on stdout. -""" - -import os -import sys -import zipfile - - -def extract(zip_path: str, dest: str) -> str: - """Extract the zip, renaming only the top-level template directory.""" - with zipfile.ZipFile(zip_path) as zf: - for info in zf.infolist(): - parts = info.filename.split("/") - - safe = [] - for i, part in enumerate(parts): - # Index 0 is the repo root (e.g. ai-native-python-). - # Index 1 is the template directory with NTFS-illegal chars. - if i == 1 and "{{cookiecutter." in part: - safe.append("{{cookiecutter.project_name}}") - else: - safe.append(part) - - target = os.path.join(dest, *[s for s in safe if s]) - - if info.is_dir(): - os.makedirs(target, exist_ok=True) - else: - os.makedirs(os.path.dirname(target), exist_ok=True) - with zf.open(info) as src, open(target, "wb") as dst: - dst.write(src.read()) - - return os.path.join(dest, os.listdir(dest)[0]) - - -if __name__ == "__main__": - if len(sys.argv) != 3: - print(f"Usage: {sys.argv[0]} ", file=sys.stderr) - sys.exit(1) - print(extract(sys.argv[1], sys.argv[2])) diff --git a/tests/test_cookiecutter.py b/tests/test_cookiecutter.py index fdf9a03..71cf765 100755 --- a/tests/test_cookiecutter.py +++ b/tests/test_cookiecutter.py @@ -220,11 +220,13 @@ def test_autofix_hook(cookies, context): "-invalid", # starts with dash "9project", # starts with number "!invalid", # starts with special character + "My Project", # contains space + "has space", # contains space ], ) def test_invalid_project_name_validation(cookies, invalid_name): """ - Test that project names starting with non-alphabetical characters are rejected + Test that project names with invalid characters are rejected """ result = cookies.bake(extra_context={"project_name": invalid_name}) @@ -238,7 +240,7 @@ def test_invalid_project_name_validation(cookies, invalid_name): [ "ValidProject", # starts with uppercase "validproject", # starts with lowercase - "My Project", # starts with uppercase, has space + "My-Project", # starts with uppercase, has hyphen "a1234", # starts with lowercase, has numbers "Z_project", # starts with uppercase, has underscore ], diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.cursor/rules/project.mdc" b/{{cookiecutter.project_name}}/.cursor/rules/project.mdc similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.cursor/rules/project.mdc" rename to {{cookiecutter.project_name}}/.cursor/rules/project.mdc diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.cursor/rules/testing.mdc" b/{{cookiecutter.project_name}}/.cursor/rules/testing.mdc similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.cursor/rules/testing.mdc" rename to {{cookiecutter.project_name}}/.cursor/rules/testing.mdc diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.dockerignore" b/{{cookiecutter.project_name}}/.dockerignore similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.dockerignore" rename to {{cookiecutter.project_name}}/.dockerignore diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/.grant.yml" b/{{cookiecutter.project_name}}/.github/.grant.yml similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/.grant.yml" rename to {{cookiecutter.project_name}}/.github/.grant.yml diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/CODEOWNERS" b/{{cookiecutter.project_name}}/.github/CODEOWNERS similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/CODEOWNERS" rename to {{cookiecutter.project_name}}/.github/CODEOWNERS diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/ISSUE_TEMPLATE.md" b/{{cookiecutter.project_name}}/.github/ISSUE_TEMPLATE.md similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/ISSUE_TEMPLATE.md" rename to {{cookiecutter.project_name}}/.github/ISSUE_TEMPLATE.md diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/PULL_REQUEST_TEMPLATE.md" b/{{cookiecutter.project_name}}/.github/PULL_REQUEST_TEMPLATE.md similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/PULL_REQUEST_TEMPLATE.md" rename to {{cookiecutter.project_name}}/.github/PULL_REQUEST_TEMPLATE.md diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/actions/bootstrap/action.yml" b/{{cookiecutter.project_name}}/.github/actions/bootstrap/action.yml similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/actions/bootstrap/action.yml" rename to {{cookiecutter.project_name}}/.github/actions/bootstrap/action.yml diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/copilot-instructions.md" b/{{cookiecutter.project_name}}/.github/copilot-instructions.md similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/copilot-instructions.md" rename to {{cookiecutter.project_name}}/.github/copilot-instructions.md diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/dependabot.yml" b/{{cookiecutter.project_name}}/.github/dependabot.yml similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/dependabot.yml" rename to {{cookiecutter.project_name}}/.github/dependabot.yml diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/etc/dictionary.txt" b/{{cookiecutter.project_name}}/.github/etc/dictionary.txt similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/etc/dictionary.txt" rename to {{cookiecutter.project_name}}/.github/etc/dictionary.txt diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/linters/cspell.config.js" b/{{cookiecutter.project_name}}/.github/linters/cspell.config.js similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/linters/cspell.config.js" rename to {{cookiecutter.project_name}}/.github/linters/cspell.config.js diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/linters/lychee.toml" b/{{cookiecutter.project_name}}/.github/linters/lychee.toml similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/linters/lychee.toml" rename to {{cookiecutter.project_name}}/.github/linters/lychee.toml diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/ci.yml" b/{{cookiecutter.project_name}}/.github/workflows/ci.yml similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/ci.yml" rename to {{cookiecutter.project_name}}/.github/workflows/ci.yml diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/commit.yml" b/{{cookiecutter.project_name}}/.github/workflows/commit.yml similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/commit.yml" rename to {{cookiecutter.project_name}}/.github/workflows/commit.yml diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/release.yml" b/{{cookiecutter.project_name}}/.github/workflows/release.yml similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/release.yml" rename to {{cookiecutter.project_name}}/.github/workflows/release.yml diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/security.yml" b/{{cookiecutter.project_name}}/.github/workflows/security.yml similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/security.yml" rename to {{cookiecutter.project_name}}/.github/workflows/security.yml diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/update.yml" b/{{cookiecutter.project_name}}/.github/workflows/update.yml similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/update.yml" rename to {{cookiecutter.project_name}}/.github/workflows/update.yml diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/validate_pr_titles.yml" b/{{cookiecutter.project_name}}/.github/workflows/validate_pr_titles.yml similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/validate_pr_titles.yml" rename to {{cookiecutter.project_name}}/.github/workflows/validate_pr_titles.yml diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.gitignore" b/{{cookiecutter.project_name}}/.gitignore similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.gitignore" rename to {{cookiecutter.project_name}}/.gitignore diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.pre-commit-config.yaml" b/{{cookiecutter.project_name}}/.pre-commit-config.yaml similarity index 97% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/.pre-commit-config.yaml" rename to {{cookiecutter.project_name}}/.pre-commit-config.yaml index 0b757a8..8b591c6 100644 --- "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.pre-commit-config.yaml" +++ b/{{cookiecutter.project_name}}/.pre-commit-config.yaml @@ -1,5 +1,5 @@ --- -exclude: '^{{ cookiecutter.project_name|replace(" ", "") }}/.*' +exclude: '^{{ cookiecutter.project_name }}/.*' repos: - repo: https://github.com/astral-sh/ruff-pre-commit rev: 3db93a2be6f214ed722bf7bce095ec1b1715422a # frozen: v0.14.2 diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/CLAUDE.md" b/{{cookiecutter.project_name}}/CLAUDE.md similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/CLAUDE.md" rename to {{cookiecutter.project_name}}/CLAUDE.md diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/CONTRIBUTING.md" b/{{cookiecutter.project_name}}/CONTRIBUTING.md similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/CONTRIBUTING.md" rename to {{cookiecutter.project_name}}/CONTRIBUTING.md diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/Dockerfile" b/{{cookiecutter.project_name}}/Dockerfile similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/Dockerfile" rename to {{cookiecutter.project_name}}/Dockerfile diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/FAQ.md" b/{{cookiecutter.project_name}}/FAQ.md similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/FAQ.md" rename to {{cookiecutter.project_name}}/FAQ.md diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/LICENSE" b/{{cookiecutter.project_name}}/LICENSE similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/LICENSE" rename to {{cookiecutter.project_name}}/LICENSE diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/README.md" b/{{cookiecutter.project_name}}/README.md similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/README.md" rename to {{cookiecutter.project_name}}/README.md diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/SECURITY.md" b/{{cookiecutter.project_name}}/SECURITY.md similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/SECURITY.md" rename to {{cookiecutter.project_name}}/SECURITY.md diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/Taskfile.yml" b/{{cookiecutter.project_name}}/Taskfile.yml similarity index 98% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/Taskfile.yml" rename to {{cookiecutter.project_name}}/Taskfile.yml index 2a9b823..c218022 100644 --- "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/Taskfile.yml" +++ b/{{cookiecutter.project_name}}/Taskfile.yml @@ -173,8 +173,8 @@ tasks: update: desc: Update the project dev and runtime dependencies cmds: - # This currently assumes uv was installed via uv (locally); we will want to make it more flexible in the future - - '{{ '{{if ne .GITHUB_ACTIONS "true"}}' }}brew upgrade uv{{ '{{end}}' }}' + # Upgrade uv via brew on macOS/Linux when running locally (not CI) + - '{{ '{{if and (ne .GITHUB_ACTIONS "true") (ne OS "windows")}}' }}brew upgrade uv{{ '{{end}}' }}' - uv tool upgrade --all - pre-commit autoupdate --freeze --jobs 4 - uv lock --upgrade diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/pyproject.toml" b/{{cookiecutter.project_name}}/pyproject.toml similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/pyproject.toml" rename to {{cookiecutter.project_name}}/pyproject.toml diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/renovate.json" b/{{cookiecutter.project_name}}/renovate.json similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/renovate.json" rename to {{cookiecutter.project_name}}/renovate.json diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/get_epoch.sh" b/{{cookiecutter.project_name}}/scripts/get_epoch.sh similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/get_epoch.sh" rename to {{cookiecutter.project_name}}/scripts/get_epoch.sh diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/get_os.sh" b/{{cookiecutter.project_name}}/scripts/get_os.sh similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/get_os.sh" rename to {{cookiecutter.project_name}}/scripts/get_os.sh diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/get_platform.sh" b/{{cookiecutter.project_name}}/scripts/get_platform.sh similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/get_platform.sh" rename to {{cookiecutter.project_name}}/scripts/get_platform.sh diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/get_rfc3339_timestamp.py" b/{{cookiecutter.project_name}}/scripts/get_rfc3339_timestamp.py similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/get_rfc3339_timestamp.py" rename to {{cookiecutter.project_name}}/scripts/get_rfc3339_timestamp.py diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/scan_image.py" b/{{cookiecutter.project_name}}/scripts/scan_image.py similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/scan_image.py" rename to {{cookiecutter.project_name}}/scripts/scan_image.py diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/scan_workflow_logs.sh" b/{{cookiecutter.project_name}}/scripts/scan_workflow_logs.sh similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/scan_workflow_logs.sh" rename to {{cookiecutter.project_name}}/scripts/scan_workflow_logs.sh diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/validate_service_definition.py" b/{{cookiecutter.project_name}}/scripts/validate_service_definition.py similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/scripts/validate_service_definition.py" rename to {{cookiecutter.project_name}}/scripts/validate_service_definition.py diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/service_definition.yaml" b/{{cookiecutter.project_name}}/service_definition.yaml similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/service_definition.yaml" rename to {{cookiecutter.project_name}}/service_definition.yaml diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/src/main.py" b/{{cookiecutter.project_name}}/src/main.py similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/src/main.py" rename to {{cookiecutter.project_name}}/src/main.py diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/src/{{cookiecutter.project_slug}}/__init__.py" b/{{cookiecutter.project_name}}/src/{{cookiecutter.project_slug}}/__init__.py similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/src/{{cookiecutter.project_slug}}/__init__.py" rename to {{cookiecutter.project_name}}/src/{{cookiecutter.project_slug}}/__init__.py diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/src/{{cookiecutter.project_slug}}/config.py" b/{{cookiecutter.project_name}}/src/{{cookiecutter.project_slug}}/config.py similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/src/{{cookiecutter.project_slug}}/config.py" rename to {{cookiecutter.project_name}}/src/{{cookiecutter.project_slug}}/config.py diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/src/{{cookiecutter.project_slug}}/constants.py" b/{{cookiecutter.project_name}}/src/{{cookiecutter.project_slug}}/constants.py similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/src/{{cookiecutter.project_slug}}/constants.py" rename to {{cookiecutter.project_name}}/src/{{cookiecutter.project_slug}}/constants.py diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/tests/__init__.py" b/{{cookiecutter.project_name}}/tests/__init__.py similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/tests/__init__.py" rename to {{cookiecutter.project_name}}/tests/__init__.py diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/tests/test_config.py" b/{{cookiecutter.project_name}}/tests/test_config.py similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/tests/test_config.py" rename to {{cookiecutter.project_name}}/tests/test_config.py diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/tests/test_integration.py" b/{{cookiecutter.project_name}}/tests/test_integration.py similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/tests/test_integration.py" rename to {{cookiecutter.project_name}}/tests/test_integration.py diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/tests/test_main.py" b/{{cookiecutter.project_name}}/tests/test_main.py similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/tests/test_main.py" rename to {{cookiecutter.project_name}}/tests/test_main.py diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/tests/test_package.py" b/{{cookiecutter.project_name}}/tests/test_package.py similarity index 100% rename from "{{cookiecutter.project_name|replace(\" \", \"\")}}/tests/test_package.py" rename to {{cookiecutter.project_name}}/tests/test_package.py