-
Notifications
You must be signed in to change notification settings - Fork 2
feat: add mk template for scaffolding project structures #40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
f874908
6700684
e8fb5c4
128a0dd
351cb79
05e9656
f3c0163
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -838,6 +838,55 @@ else | |||||
| ((PASSED++)) | ||||||
| fi | ||||||
|
|
||||||
| # Test: mk template python | ||||||
| cd "$TEST_DIR" | ||||||
| u7 mk template python myapp >/dev/null 2>&1 | ||||||
| if [[ -f "myapp/src/main.py" && -f "myapp/README.md" && -f "myapp/tests/__init__.py" ]]; then | ||||||
| echo -e "${GREEN}✓${NC} mk template python works" | ||||||
| ((PASSED++)) | ||||||
| else | ||||||
| echo -e "${RED}✗${NC} mk template python failed" | ||||||
greptile-apps[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| ((FAILED++)) | ||||||
| fi | ||||||
|
|
||||||
| # Test: mk template node | ||||||
| u7 mk template node mynode >/dev/null 2>&1 | ||||||
| if [[ -f "mynode/package.json" && -f "mynode/src/index.js" ]]; then | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test for the Node.js template is incomplete. The scaffolding logic also creates a
Suggested change
|
||||||
| assert_contains "mk template node package.json has name" "mynode" "$(cat mynode/package.json)" | ||||||
| else | ||||||
| echo -e "${RED}✗${NC} mk template node failed" | ||||||
| ((FAILED++)) | ||||||
| fi | ||||||
|
|
||||||
| # Test: mk template bash | ||||||
| u7 mk template bash mybash >/dev/null 2>&1 | ||||||
| if [[ -f "mybash/main.sh" && -x "mybash/main.sh" ]]; then | ||||||
| echo -e "${GREEN}✓${NC} mk template bash works" | ||||||
| ((PASSED++)) | ||||||
| else | ||||||
| echo -e "${RED}✗${NC} mk template bash failed" | ||||||
| ((FAILED++)) | ||||||
| fi | ||||||
|
|
||||||
| # Test: mk template web | ||||||
| u7 mk template web myweb >/dev/null 2>&1 | ||||||
| if [[ -f "myweb/index.html" && -d "myweb/css" && -d "myweb/js" ]]; then | ||||||
| echo -e "${GREEN}✓${NC} mk template web works" | ||||||
| ((PASSED++)) | ||||||
| else | ||||||
| echo -e "${RED}✗${NC} mk template web failed" | ||||||
| ((FAILED++)) | ||||||
| fi | ||||||
|
|
||||||
| # Test: mk template requires args | ||||||
| result=$(u7 mk template 2>&1) | ||||||
| assert_contains "mk template requires args" "Usage:" "$result" | ||||||
|
|
||||||
| # Test: mk template rejects existing directory | ||||||
| mkdir -p existing_dir | ||||||
| result=$(u7 mk template python existing_dir 2>&1) | ||||||
| assert_contains "mk template rejects existing dir" "already exists" "$result" | ||||||
|
|
||||||
| # Cleanup | ||||||
| cd / | ||||||
| rm -rf "$TEST_DIR" | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -526,6 +526,60 @@ _u7_make() { | |||||||||||||
| fi | ||||||||||||||
| ;; | ||||||||||||||
|
|
||||||||||||||
| template) | ||||||||||||||
| local tmpl="$1" | ||||||||||||||
| local name="$2" | ||||||||||||||
| if [[ -z "$tmpl" || -z "$name" ]]; then | ||||||||||||||
| echo "Usage: u7 mk template <python|node|bash|web> <project-name>" | ||||||||||||||
| return 1 | ||||||||||||||
| fi | ||||||||||||||
greptile-apps[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
| if [[ "$name" == *..* || "$name" == /* ]]; then | ||||||||||||||
| echo "Error: project name must not contain '..' or start with '/'" | ||||||||||||||
| return 1 | ||||||||||||||
| fi | ||||||||||||||
| if [[ -d "$name" ]]; then | ||||||||||||||
| echo "Error: directory '$name' already exists" | ||||||||||||||
| return 1 | ||||||||||||||
| fi | ||||||||||||||
greptile-apps[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
| if [[ "$_U7_DRY_RUN" == "1" ]]; then | ||||||||||||||
| echo "[dry-run] Create $tmpl project: $name" | ||||||||||||||
| return 0 | ||||||||||||||
| fi | ||||||||||||||
| case "$tmpl" in | ||||||||||||||
| python) | ||||||||||||||
| mkdir -p "$name/src" "$name/tests" | ||||||||||||||
| echo "# $name" > "$name/README.md" | ||||||||||||||
| echo "#!/usr/bin/env python3" > "$name/src/main.py" | ||||||||||||||
| touch "$name/src/__init__.py" "$name/tests/__init__.py" | ||||||||||||||
| echo "Created Python project: $name" | ||||||||||||||
| ;; | ||||||||||||||
| node) | ||||||||||||||
| _u7_require jq || return 1 | ||||||||||||||
| mkdir -p "$name/src" "$name/test" | ||||||||||||||
| echo "# $name" > "$name/README.md" | ||||||||||||||
| jq -n --arg name "$name" '{"name": $name, "version": "0.1.0", "main": "src/index.js"}' > "$name/package.json" | ||||||||||||||
| echo "// $name" > "$name/src/index.js" | ||||||||||||||
| echo "Created Node project: $name" | ||||||||||||||
| ;; | ||||||||||||||
greptile-apps[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
| bash) | ||||||||||||||
| mkdir -p "$name" | ||||||||||||||
| echo "# $name" > "$name/README.md" | ||||||||||||||
| printf '#!/usr/bin/env bash\nset -euo pipefail\n\necho "Hello from %s"\n' "$name" > "$name/main.sh" | ||||||||||||||
greptile-apps[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
greptile-apps[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||
| chmod +x "$name/main.sh" | ||||||||||||||
| echo "Created Bash project: $name" | ||||||||||||||
| ;; | ||||||||||||||
| web) | ||||||||||||||
| mkdir -p "$name/css" "$name/js" | ||||||||||||||
| local escaped_name="${name//</<}" | ||||||||||||||
| escaped_name="${escaped_name//>/>}" | ||||||||||||||
|
Comment on lines
+591
to
+592
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The
Suggested change
Removing the dead escaping also makes it immediately obvious that the name is safe to embed directly, thanks to the earlier validation guard. Prompt To Fix With AIThis is a comment left during a code review.
Path: utility.sh
Line: 573-574
Comment:
**HTML escaping is now dead code**
The `<` → `<` and `>` → `>` substitutions on these two lines can never trigger because the input validation on line 536 (`^[a-zA-Z0-9_-]+$`) already rejects any name containing `<`, `>`, or `&`. The `escaped_name` variable is identical to `$name` in every reachable code path, so the variable and both assignments can be removed:
```suggestion
echo "<!DOCTYPE html><html><head><title>$name</title><link rel=\"stylesheet\" href=\"css/style.css\"></head><body><h1>$name</h1><script src=\"js/main.js\"></script></body></html>" > "$name/index.html"
```
Removing the dead escaping also makes it immediately obvious that the name is safe to embed directly, thanks to the earlier validation guard.
How can I resolve this? If you propose a fix, please make it concise. |
||||||||||||||
| echo "<!DOCTYPE html><html><head><title>$escaped_name</title><link rel=\"stylesheet\" href=\"css/style.css\"></head><body><h1>$escaped_name</h1><script src=\"js/main.js\"></script></body></html>" > "$name/index.html" | ||||||||||||||
|
Comment on lines
+591
to
+593
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The previous fix added <title>foo & bar</title>
<h1>foo & bar</h1>A bare
Suggested change
Prompt To Fix With AIThis is a comment left during a code review.
Path: utility.sh
Line: 569-571
Comment:
**`&` not escaped in HTML template (incomplete fix)**
The previous fix added `<` → `<` and `>` → `>` escaping, but `&` → `&` is still missing. This means a project name like `foo & bar` will produce:
```html
<title>foo & bar</title>
<h1>foo & bar</h1>
```
A bare `&` not followed by a valid entity reference is technically invalid HTML and can cause browsers to issue parse warnings. Additionally, the `&` substitution **must happen first** — if it were added after the current `<`/`>` substitutions, the `<` and `>` sequences already written would get double-encoded into `&lt;` / `&gt;`.
```suggestion
local escaped_name="${name//&/&}"
escaped_name="${escaped_name//</<}"
escaped_name="${escaped_name//>/>}"
```
How can I resolve this? If you propose a fix, please make it concise. |
||||||||||||||
| touch "$name/css/style.css" "$name/js/main.js" | ||||||||||||||
| echo "Created Web project: $name" | ||||||||||||||
| ;; | ||||||||||||||
| *) echo "Unknown template: $tmpl. Available: python, node, bash, web" ; return 1 ;; | ||||||||||||||
| esac | ||||||||||||||
| ;; | ||||||||||||||
|
|
||||||||||||||
| sequence) | ||||||||||||||
| if [[ "$1" != "with" || "$2" != "prefix" ]]; then | ||||||||||||||
| echo "Usage: u7 mk sequence with prefix <prefix> limit <N>" | ||||||||||||||
|
|
@@ -557,6 +611,7 @@ Entities: | |||||||||||||
| link <source> to <destination> Create symbolic link | ||||||||||||||
| archive <output> from <files...> Create archive from <files...> to <output> | ||||||||||||||
| clone <repo> [to <directory>] Git clone a repository | ||||||||||||||
| template <python|node|bash|web> <name> Scaffold a project structure | ||||||||||||||
| sequence with prefix <prefix> limit <N> Generate numbered sequence with prefix <prefix> and limit <N> | ||||||||||||||
| EOF | ||||||||||||||
| ;; | ||||||||||||||
|
|
||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test for the Python template is incomplete. The scaffolding logic creates
src/__init__.py, but this check doesn't verify its existence. Adding this check will make the test more thorough and prevent future regressions.