From 63489e0983a72fccb18df24c886335bb219551cb Mon Sep 17 00:00:00 2001 From: Juan-Pablo Velez Date: Sat, 15 Nov 2025 00:27:21 +0000 Subject: [PATCH] Add Oh My Zsh and plugins to devcontainer Fixes #9 This commit adds Oh My Zsh with useful plugins to enhance the shell experience in devcontainers. The following changes were made: - Added zsh feature from ghcr.io/nils-geistmann/devcontainers-features/zsh:1 - Configured plugins: git, colored-man-pages, colorize, history, zsh-autosuggestions, zsh-completions, zsh-syntax-highlighting - Applied to both template devcontainer and project's own devcontainer - Fixed pre-existing bug with double comma in VS Code extensions array - Added test to verify Zsh feature is included in generated projects - Updated test helper to use --vcs-ref=HEAD for testing uncommitted changes All tests pass and pre-commit hooks are satisfied. --- .devcontainer/devcontainer.json | 3 ++ README.md | 6 ++-- copier-helper.sh | 0 copier.yml | 2 +- docs/index.md | 4 +-- docs/promp_arguments.md | 7 ++-- docs/tutorial.md | 4 +-- .../.devcontainer/devcontainer.json.jinja | 5 ++- ...e %}checkREnvironment.ps1{% endif %}.jinja | 2 +- ...ce %}checkREnvironment.sh{% endif %}.jinja | 2 +- ...f python %}pyproject.toml{% endif %}.jinja | 2 +- tests/test_template.py | 32 +++++++++++++++++++ 12 files changed, 53 insertions(+), 16 deletions(-) mode change 100644 => 100755 copier-helper.sh diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 1fcff6d..83e124d 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,6 +2,9 @@ "name": "Switchbox Copier Template", "image": "mcr.microsoft.com/devcontainers/python:1-3.13-bookworm", "features": { + "ghcr.io/nils-geistmann/devcontainers-features/zsh:1": { + "plugins": "git colored-man-pages colorize history zsh-autosuggestions zsh-completions zsh-syntax-highlighting" + }, "ghcr.io/guiyomh/features/just:0.1.0": { "version": "1.42.4" } }, "postCreateCommand": "./.devcontainer/postCreateCommand.sh", diff --git a/README.md b/README.md index b0b6a56..b1ed45c 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ **Documentation**: -This Coper template lets you bootstrap python packages or polyglot data science projects. +This Coper template lets you bootstrap python packages or polyglot data science projects. For python packages, it supports: - Modern python tooling: uv, ruff, ty, pytest, tox @@ -18,7 +18,7 @@ For R data science projects, it supports: - Tidyverse libaries: ggplot, dplyr, etc. - Quarto notebooks - Modern R tooling: pak, air, radian -- Fast install of compiled R packages via pak and P3M +- Fast install of compiled R packages via pak and P3M - VS Code extensions for R All project types include: @@ -58,7 +58,7 @@ After making your selections, copier will: If you want to skip copier's interactive prompts and generate the project directly, you can pass arguments directly. -For instance, the following prompt generates a python package + data science project: +For instance, the following prompt generates a python package + data science project: ```bash copier copy --defaults --force \ diff --git a/copier-helper.sh b/copier-helper.sh old mode 100644 new mode 100755 diff --git a/copier.yml b/copier.yml index a31bcc7..e648545 100644 --- a/copier.yml +++ b/copier.yml @@ -76,7 +76,7 @@ r_data_science: default: "{{ 'r_data_science' in project_features }}" when: false -# Directory name computed from destination path +# Directory name computed from destination path project_name: type: str default: "{{ _copier_conf.dst_path.name }}" diff --git a/docs/index.md b/docs/index.md index 5f6fc56..3cbebdd 100644 --- a/docs/index.md +++ b/docs/index.md @@ -48,10 +48,10 @@ On your local machine, navigate to the directory in which you want to create you copier copy https://github.com/switchbox-data/switchbox-copier-template ``` -Where `` is the name you want to give your project. This will be used as the directory name, and as the name of your repo if you use GitHub. +Where `` is the name you want to give your project. This will be used as the directory name, and as the name of your repo if you use GitHub. Follow the prompts to configure your project. Once completed, a new directory containing your project will be created. Then navigate into your newly created project directory and follow the instructions in the `README.md` to complete the setup of your project. ## Acknowledgements -This project is inspired by the [cookiecutter-uv](https://fpgmaas.github.io/cookiecutter-uv/) package. \ No newline at end of file +This project is inspired by the [cookiecutter-uv](https://fpgmaas.github.io/cookiecutter-uv/) package. diff --git a/docs/promp_arguments.md b/docs/promp_arguments.md index 10e9a20..9b23b9f 100644 --- a/docs/promp_arguments.md +++ b/docs/promp_arguments.md @@ -28,8 +28,8 @@ This is a **multiselect** option where you can choose one or more project types: - Polars, PyArrow, Seaborn, NumPy for data analysis - Quarto notebooks for reproducible research - Modern Python tooling (uv, ruff, ty) - - + + - `r_data_science`: Sets up an R data science environment with: - Tidyverse libraries (ggplot2, dplyr, etc.) - Quarto notebooks @@ -41,7 +41,7 @@ This is a **multiselect** option where you can choose one or more project types: - Testing framework (pytest, tox) - Documentation (MkDocs with Material theme) - Publishing workflow (PyPI) - + You can select multiple features! Here are some common combinations: *Python data science project*: @@ -107,4 +107,3 @@ The template automatically computes several values based on your choices: - Python package names - Import statements: `from project_slug import module` - Directory structures - diff --git a/docs/tutorial.md b/docs/tutorial.md index 61f3ccf..21eb454 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -3,7 +3,7 @@ This page contains a complete tutorial on how to create your project. ## Step 1: Install copier -To start, we will need to install `copier`. +To start, we will need to install `copier`. We recommend doing so with `uv`. If you don't have `uv`, installation instructions can be found [here](https://docs.astral.sh/uv/#getting-started). For MacOS or Linux; @@ -87,4 +87,4 @@ Now we commit the changes made by the two steps above to the repository: git add . git commit -m 'Fix formatting issues' git push origin main -``` \ No newline at end of file +``` diff --git a/template/.devcontainer/devcontainer.json.jinja b/template/.devcontainer/devcontainer.json.jinja index 41a1a58..511524f 100644 --- a/template/.devcontainer/devcontainer.json.jinja +++ b/template/.devcontainer/devcontainer.json.jinja @@ -2,6 +2,9 @@ "name": "{{ project_name }}", "image": "mcr.microsoft.com/devcontainers/base:ubuntu-24.04", "features": { + "ghcr.io/nils-geistmann/devcontainers-features/zsh:1": { + "plugins": "git colored-man-pages colorize history zsh-autosuggestions zsh-completions zsh-syntax-highlighting" + }, "ghcr.io/guiyomh/features/just:0.1.0": { "version": "1.42.4" }{% if python %}, "ghcr.io/devcontainers/features/python:1": { "version": "os-provided", @@ -46,7 +49,7 @@ "nefrob.vscode-just-syntax"{% if python %}, "ms-python.python", "charliermarsh.ruff", - "astral-sh.ty",{% endif %}{% if r_data_science %}, + "astral-sh.ty"{% endif %}{% if r_data_science %}, "REditorSupport.r", "RDebugger.r-debugger", "Posit.air-vscode"{% endif %}{% if python_data_science or r_data_science %}, diff --git a/template/.devcontainer/{% if r_data_science %}checkREnvironment.ps1{% endif %}.jinja b/template/.devcontainer/{% if r_data_science %}checkREnvironment.ps1{% endif %}.jinja index 9746a32..80b1a7c 100644 --- a/template/.devcontainer/{% if r_data_science %}checkREnvironment.ps1{% endif %}.jinja +++ b/template/.devcontainer/{% if r_data_science %}checkREnvironment.ps1{% endif %}.jinja @@ -86,7 +86,7 @@ if (-not (Test-Path $pkgLockPath)) { # Install packages from lockfile if (Test-Path $pkgLockPath) { Write-Host "📦 Installing packages from pak lockfile..." -ForegroundColor Cyan - + try { Rscript -e "setwd('$projectRoot'); pak::lockfile_install(lockfile = 'pkg.lock')" if ($LASTEXITCODE -eq 0) { diff --git a/template/.devcontainer/{% if r_data_science %}checkREnvironment.sh{% endif %}.jinja b/template/.devcontainer/{% if r_data_science %}checkREnvironment.sh{% endif %}.jinja index 88cea3d..61d0d28 100644 --- a/template/.devcontainer/{% if r_data_science %}checkREnvironment.sh{% endif %}.jinja +++ b/template/.devcontainer/{% if r_data_science %}checkREnvironment.sh{% endif %}.jinja @@ -67,7 +67,7 @@ fi # Install packages from lockfile if [[ -f "$PROJECT_ROOT/pkg.lock" ]]; then echo "📦 Installing packages from pak lockfile..." - + if Rscript -e "setwd('$PROJECT_ROOT'); pak::lockfile_install(lockfile = 'pkg.lock')"; then echo "✅ Packages installed successfully from pak lockfile" else diff --git a/template/{% if python %}pyproject.toml{% endif %}.jinja b/template/{% if python %}pyproject.toml{% endif %}.jinja index 43b8e72..3a03e22 100644 --- a/template/{% if python %}pyproject.toml{% endif %}.jinja +++ b/template/{% if python %}pyproject.toml{% endif %}.jinja @@ -41,7 +41,7 @@ Documentation = "https://{{ author_github_handle }}.github.io/{{ project_name }} dev = [ "pytest>=7.2.0", "ruff>=0.11.5", - "ty>=0.0.1a21", + "ty>=0.0.1a21", "deptry>=0.20.0", {%- if python_package %} "mkdocs>=1.6.0", diff --git a/tests/test_template.py b/tests/test_template.py index 5a4476b..6c85dd3 100644 --- a/tests/test_template.py +++ b/tests/test_template.py @@ -12,6 +12,7 @@ def run_copier(template_dir: Path, dest: Path, data: dict[str, str]) -> subproce "--defaults", "--force", "--trust", + "--vcs-ref=HEAD", ] for k, v in data.items(): args.extend(["--data", f"{k}={v}"]) @@ -213,3 +214,34 @@ def test_project_name_in_devcontainer(tmp_path: Path) -> None: # Should NOT contain the directory name in paths content = (dest / ".devcontainer/devcontainer.json").read_text() assert "/workspaces/my_custom_dir/" not in content + + +def test_devcontainer_zsh_feature(tmp_path: Path) -> None: + """Test that Oh My Zsh feature is included in devcontainer configuration.""" + dest = tmp_path / "zsh_test" + res = run_copier( + Path(__file__).parents[1], + dest, + { + "author": "Test", + "email": "test@example.com", + "author_github_handle": "test", + "project_name": "zsh-test-proj", + "project_description": "Test Zsh configuration", + "project_features": "[python_data_science]", + "use_github": True, + "open_source_license": "MIT license", + "aws": False, + }, + ) + assert res.returncode == 0, res.stderr + # Check that devcontainer includes the Zsh feature + assert_file_contains( + dest, ".devcontainer/devcontainer.json", '"ghcr.io/nils-geistmann/devcontainers-features/zsh:1"' + ) + # Check that the Zsh plugins are configured + assert_file_contains( + dest, + ".devcontainer/devcontainer.json", + "git colored-man-pages colorize history zsh-autosuggestions zsh-completions zsh-syntax-highlighting", + )