From 2cf40f13f1b43a9b2d5c10cf320894fbf3765dfc Mon Sep 17 00:00:00 2001 From: Jonatan Mata Date: Wed, 30 Apr 2025 12:54:20 -0600 Subject: [PATCH 1/2] feat: unified shell + sandbox setup with Copier, devcontainer CLI, and robust features Signed-off-by: Jonatan Mata --- .copier-answers.yml | 3 + .gitignore | 1 + .template/copier.yaml | 10 ++ .../.devcontainer/devcontainer.json.jinja | 14 ++ Makefile | 158 +++++++++++------- src/shell/devcontainer-feature.json | 10 ++ src/shell/install.sh | 111 ++++++++++-- 7 files changed, 232 insertions(+), 75 deletions(-) create mode 100644 .copier-answers.yml create mode 100644 .template/copier.yaml create mode 100644 .template/devcontainer_template/.devcontainer/devcontainer.json.jinja diff --git a/.copier-answers.yml b/.copier-answers.yml new file mode 100644 index 0000000..cef8dc9 --- /dev/null +++ b/.copier-answers.yml @@ -0,0 +1,3 @@ +features: + - shell +image: debian:latest diff --git a/.gitignore b/.gitignore index 02b227f..fa3dd0e 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ Thumbs.db *.swo .sandbox/ +.venv-tools/ diff --git a/.template/copier.yaml b/.template/copier.yaml new file mode 100644 index 0000000..705dda8 --- /dev/null +++ b/.template/copier.yaml @@ -0,0 +1,10 @@ +_subdirectory: devcontainer_template +_questions: + features: + type: yaml + help: "List of features to include" + default: ["shell"] + image: + type: str + help: "Base image to use" + default: "ubuntu:latest" diff --git a/.template/devcontainer_template/.devcontainer/devcontainer.json.jinja b/.template/devcontainer_template/.devcontainer/devcontainer.json.jinja new file mode 100644 index 0000000..e93224c --- /dev/null +++ b/.template/devcontainer_template/.devcontainer/devcontainer.json.jinja @@ -0,0 +1,14 @@ +{ + "name": "Feature Test Sandbox", + "image": "{{ image }}", + "features": { + {% for feature in features %} + "./features/{{ feature }}": { + {% if feature == "shell" %} + "timezone": "America/Costa_Rica", + "opinionated": true + {% endif %} + }{% if not loop.last %},{% endif %} + {% endfor %} + } +} \ No newline at end of file diff --git a/Makefile b/Makefile index 76ee3e4..1b9b01b 100644 --- a/Makefile +++ b/Makefile @@ -1,71 +1,122 @@ +# Configuration SANDBOX_DIR = .sandbox +SANDBOX_ANSWERS_FILE = $(SANDBOX_DIR)/answers.yml +COPIER_VENV = .venv-tools +COPIER_BIN = $(COPIER_VENV)/bin/copier FEATURES ?= shell IMAGE ?= ubuntu:latest IMAGES = ubuntu:latest debian:latest amazonlinux:2023 mcr.microsoft.com/devcontainers/base:ubuntu +ANSWERS_FILE = .copier-answers.yml -.PHONY: help sandbox clean build up rebuild exec stop logs interactive +.PHONY: help sandbox clean build up rebuild exec stop logs interactive ensure-copier format validate +# Help Documentation help: @echo "" - @echo "\033[1;36mAvailable make commands:\033[0m" - @echo "--------------------------------------" - @echo "\033[1;36mSandbox Management:\033[0m" - @printf " %-28s - %s\n" "make sandbox" "Create sandbox for features" - @printf " %-28s - %s\n" "make clean" "Remove sandbox" + @echo "Available make commands" + @echo "------------------------" + @echo "Sandbox Management:" + @printf " %-28s %s\n" "make sandbox" "Create sandbox using Copier and answers file" + @printf " %-28s %s\n" "make interactive" "Prompt user to create .copier-answers.yml" + @printf " %-28s %s\n" "make clean" "Remove the generated sandbox directory" @echo "" - @echo "\033[1;36mDevcontainer Lifecycle:\033[0m" - @printf " %-28s - %s\n" "make devcontainer-up" "Build/start devcontainer" - @printf " %-28s - %s\n" "make build" "Build the container only (no attach)" - @printf " %-28s - %s\n" "make up" "Start/attach to running container" - @printf " %-28s - %s\n" "make rebuild" "Clean + re-sandbox + up" - @printf " %-28s - %s\n" "make stop" "Stop the devcontainer" - @printf " %-28s - %s\n" "make logs" "Show container logs" + @echo "Devcontainer Lifecycle:" + @printf " %-28s %s\n" "make devcontainer-up" "Create and run devcontainer" + @printf " %-28s %s\n" "make build" "Build devcontainer image only" + @printf " %-28s %s\n" "make up" "Alias to 'devcontainer-up'" + @printf " %-28s %s\n" "make exec CMD='zsh'" "Execute command in container" @echo "" - @echo "\033[1;36mDevcontainer Utilities:\033[0m" - @printf " %-28s - %s\n" "make exec CMD=\"zsh\"" "Execute a command in the devcontainer" - @printf " %-28s - %s\n" "make interactive" "Interactive feature/image selection" + @echo "Utility and Maintenance:" + @printf " %-28s %s\n" "make format" "Format devcontainer.json using jq" + @printf " %-28s %s\n" "make container-id" "Print running devcontainer ID" @echo "" - @echo "\033[1;36mUsage Examples:\033[0m" - @echo "--------------------------------------" - @echo " make sandbox FEATURES=\"shell hello\" IMAGE=" - @echo " make devcontainer-up FEATURES=\"shell hello\" IMAGE=" + @echo "Reference:" + @echo " - Features live under ./src/" + @echo " - Base images: $(IMAGES)" @echo "" + @echo "Examples:" + @echo " make sandbox FEATURES=\"shell hello\" IMAGE=debian:latest" + @echo " make devcontainer-up" +# List available feature folders list-features: @echo "Available features:" @cd src && find . -maxdepth 1 -mindepth 1 -type d | sed 's|^\./||' | sort | nl +# List supported base images list-images: @echo "Available base images:" @echo "$(IMAGES)" | tr ' ' '\n' | sort | nl -sandbox: - @echo "Creating sandbox environment for features: $(FEATURES) with base image: $(IMAGE)" +# Ensure Copier is installed in a local virtualenv +ensure-copier: + @echo "Checking for Copier..." + @if [ ! -x "$(COPIER_BIN)" ]; then \ + echo "Copier not found. Creating virtualenv..."; \ + if ! command -v python3 >/dev/null 2>&1; then \ + echo "python3 is required but not found. Please install it."; \ + exit 1; \ + fi; \ + python3 -m venv $(COPIER_VENV); \ + . $(COPIER_VENV)/bin/activate && pip install --upgrade pip copier; \ + echo "Copier installed in $(COPIER_VENV)"; \ + else \ + echo "Copier already available."; \ + fi + +# Generate the sandbox with Copier +sandbox: ensure-copier + @if [ ! -f $(ANSWERS_FILE) ]; then \ + echo "No $(ANSWERS_FILE) found. Running interactive setup..."; \ + $(MAKE) interactive; \ + else \ + read -p "Use existing $(ANSWERS_FILE)? [Y/n]: " CONFIRM; \ + if [ "$$CONFIRM" = "n" ] || [ "$$CONFIRM" = "N" ]; then \ + $(MAKE) interactive; \ + fi; \ + fi + @echo "Using $(ANSWERS_FILE) to generate sandbox..." rm -rf $(SANDBOX_DIR) + mkdir -p $(SANDBOX_DIR) + @echo "Copying feature definitions into sandbox..." mkdir -p $(SANDBOX_DIR)/.devcontainer/features - @for feature in $(FEATURES); do \ - cp -r src/$$feature $(SANDBOX_DIR)/.devcontainer/features/$$feature; \ - done - @echo '{' > $(SANDBOX_DIR)/.devcontainer/devcontainer.json - @echo ' "name": "Feature Test Sandbox",' >> $(SANDBOX_DIR)/.devcontainer/devcontainer.json - @echo ' "image": "$(IMAGE)",' >> $(SANDBOX_DIR)/.devcontainer/devcontainer.json - @echo ' "features": {' >> $(SANDBOX_DIR)/.devcontainer/devcontainer.json - @features_array=$(FEATURES); \ - for feature in $$features_array; do \ - echo " \"./features/$$feature\": {}," >> $(SANDBOX_DIR)/.devcontainer/devcontainer.json; \ - done - sed -i '' '$$s/,$$//' $(SANDBOX_DIR)/.devcontainer/devcontainer.json - @echo ' }' >> $(SANDBOX_DIR)/.devcontainer/devcontainer.json - @echo '}' >> $(SANDBOX_DIR)/.devcontainer/devcontainer.json - @echo "Sandbox created at $(SANDBOX_DIR)/" + cp -R src/* $(SANDBOX_DIR)/.devcontainer/features/ + cp $(ANSWERS_FILE) $(SANDBOX_ANSWERS_FILE) + $(COPIER_BIN) copy -a $(notdir $(SANDBOX_ANSWERS_FILE)) .template $(SANDBOX_DIR) + @$(MAKE) format + @jq . $(SANDBOX_DIR)/.devcontainer/devcontainer.json + + +# Prompt user to select features and base image, then generate answers file +interactive: ensure-copier + @$(MAKE) list-features + @read -p "Select feature numbers (space-separated): " NUMS; \ + FEATURES_SELECTED=""; \ + for n in $$NUMS; do \ + F=$$(cd src && find . -maxdepth 1 -mindepth 1 -type d | sed 's|^\./||' | sort | sed -n "$${n}p"); \ + FEATURES_SELECTED="$$FEATURES_SELECTED $$F"; \ + done; \ + echo ""; \ + $(MAKE) list-images; \ + read -p "Select image number: " IMG_NUM; \ + IMAGE_SELECTED=$$(echo "$(IMAGES)" | tr ' ' '\n' | sort | sed -n "$${IMG_NUM}p"); \ + echo ""; \ + echo "You selected: $$FEATURES_SELECTED for image $$IMAGE_SELECTED"; \ + echo "features:" > $(ANSWERS_FILE); \ + for f in $$FEATURES_SELECTED; do echo " - $$f"; done >> $(ANSWERS_FILE); \ + echo "image: $$IMAGE_SELECTED" >> $(ANSWERS_FILE); \ + echo "$(ANSWERS_FILE) generated." +# Clean the sandbox directory clean: @echo "Cleaning sandbox..." rm -rf $(SANDBOX_DIR) @echo "Sandbox cleaned." +# Devcontainer lifecycle commands + devcontainer-up: sandbox - @echo "Launching devcontainer for features $(FEATURES) using image $(IMAGE)..." + @echo "Launching devcontainer..." cd $(SANDBOX_DIR) && devcontainer up --workspace-folder . build: @@ -74,30 +125,15 @@ build: up: cd $(SANDBOX_DIR) && devcontainer up --workspace-folder . -rebuild: clean devcontainer-up - exec: cd $(SANDBOX_DIR) && devcontainer exec --workspace-folder . -- $(CMD) -stop: - cd $(SANDBOX_DIR) && devcontainer stop --workspace-folder . - -logs: - cd $(SANDBOX_DIR) && devcontainer logs --workspace-folder . +# Format devcontainer.json using jq +format: + @echo "Formatting devcontainer.json..." + @jq . $(SANDBOX_DIR)/.devcontainer/devcontainer.json > $(SANDBOX_DIR)/.devcontainer/devcontainer.json.tmp && \ + mv $(SANDBOX_DIR)/.devcontainer/devcontainer.json.tmp $(SANDBOX_DIR)/.devcontainer/devcontainer.json && \ + echo "Formatted successfully." -interactive: - @$(MAKE) list-features - @read -p "Select feature numbers (space-separated): " FEATURES_NUMBERS; \ - FEATURES_SELECTED=""; \ - for num in $$FEATURES_NUMBERS; do \ - FEATURE=$$(cd src && find . -maxdepth 1 -mindepth 1 -type d | sed 's|^\./||' | sort | sed -n "$${num}p"); \ - FEATURES_SELECTED="$$FEATURES_SELECTED $$FEATURE"; \ - done; \ - echo ""; \ - $(MAKE) list-images; \ - read -p "Select image number: " IMAGE_NUMBER; \ - IMAGE_SELECTED=$$(echo "$(IMAGES)" | tr ' ' '\n' | sort | sed -n "$${IMAGE_NUMBER}p"); \ - echo ""; \ - echo "Using features: $$FEATURES_SELECTED"; \ - echo "Using image: $$IMAGE_SELECTED"; \ - $(MAKE) devcontainer-up FEATURES="$$FEATURES_SELECTED" IMAGE="$$IMAGE_SELECTED" +container-id: + @docker ps --filter "name=devcontainer" --format "{{.ID}}" \ No newline at end of file diff --git a/src/shell/devcontainer-feature.json b/src/shell/devcontainer-feature.json index 44e1cf9..8a4018a 100644 --- a/src/shell/devcontainer-feature.json +++ b/src/shell/devcontainer-feature.json @@ -4,6 +4,11 @@ "name": "Shell Setup", "description": "Configurable shell setup with optional Zsh, Oh My Zsh, Powerlevel10k, autosuggestions, and syntax highlighting.", "options": { + "timezone": { + "type": "string", + "default": "UTC", + "description": "Timezone to set inside the container (e.g. 'America/Costa_Rica')." + }, "installZsh": { "type": "boolean", "default": true, @@ -19,6 +24,11 @@ "default": true, "description": "Install the Powerlevel10k theme for Zsh." }, + "nerdFont": { + "type": "boolean", + "default": true, + "description": "Install Nerd Font v3 (MesloLGS) for proper Powerlevel10k rendering." + }, "autosuggestions": { "type": "boolean", "default": true, diff --git a/src/shell/install.sh b/src/shell/install.sh index 31bf943..cbf2ef2 100644 --- a/src/shell/install.sh +++ b/src/shell/install.sh @@ -27,8 +27,10 @@ OMZ_DIR="${USER_HOME}/.oh-my-zsh" ZSH_CUSTOM="${OMZ_DIR}/custom" # Feature options +: "${TIMEZONE:=UTC}" : "${INSTALLZSH:=true}" : "${OHMYZSH:=true}" +: "${NERDFONT:=true}" : "${POWERLEVEL10K:=true}" : "${AUTOSUGGESTIONS:=true}" : "${SYNTAXHIGHLIGHTING:=true}" @@ -143,6 +145,28 @@ install_powerlevel10k() { fi } +install_nerd_font() { + local font_name="Meslo" + local font_version="v3.0.2" + local download_url="https://github.com/ryanoasis/nerd-fonts/releases/download/${font_version}/${font_name}.zip" + local install_dir="${USER_HOME}/.local/share/fonts" + + echo "Installing ${font_name} Nerd Font v3..." + + install_package_if_missing curl + install_package_if_missing unzip + install_package_if_missing fc-cache || install_package_if_missing fontconfig + + mkdir -p "$install_dir" + curl -fLo "/tmp/${font_name}.zip" --retry 3 --retry-delay 2 --location "$download_url" + unzip -o "/tmp/${font_name}.zip" -d "$install_dir" + fc-cache -f "$install_dir" + + chown -R "${USERNAME}:${USERNAME}" "$install_dir" + + echo "${font_name} installed to $install_dir" +} + install_autosuggestions() { local plugin_dir="${ZSH_CUSTOM}/plugins/zsh-autosuggestions" if [ ! -d "${plugin_dir}" ]; then @@ -168,28 +192,33 @@ fix_permissions() { } apply_opinionated_files() { - echo "> Applying opinionated config files..." + echo "Applying .zshrc and .p10k.zsh configuration..." + + local default_zshrc_url="https://gist.githubusercontent.com/jonmatum/55a5ff475dbb3c5854d5ddd40dc5961d/raw/ccdd446307f915627691e58a935af8246cfd4aeb/.zshrc" + local default_p10k_url="https://gist.githubusercontent.com/jonmatum/c0b7c317f281b03bb857e425fe23f9d4/raw/031c6099a176989cc0ecbc24f097c7d547195e08/.p10k.zsh" + + local final_zshrc_url="${ZSHRCURL:-$default_zshrc_url}" + local final_p10k_url="${P10KURL:-$default_p10k_url}" - # Download custom .zshrc if provided - if [ -n "$ZSHRCURL" ]; then - echo "Downloading custom .zshrc from $ZSHRCURL" - curl -fsSL "$ZSHRCURL" -o "${USER_HOME}/.zshrc" + if curl -fsSL "$final_zshrc_url" -o "${USER_HOME}/.zshrc"; then + echo ".zshrc downloaded from $final_zshrc_url" chown "${USERNAME}:${USERNAME}" "${USER_HOME}/.zshrc" else - echo "No custom .zshrc URL provided. Skipping." + echo "Failed to download .zshrc from $final_zshrc_url" fi - # Download custom .p10k.zsh if provided - if [ -n "$P10KURL" ]; then - echo "Downloading custom .p10k.zsh from $P10KURL" - curl -fsSL "$P10KURL" -o "${USER_HOME}/.p10k.zsh" + if curl -fsSL "$final_p10k_url" -o "${USER_HOME}/.p10k.zsh"; then + echo ".p10k.zsh downloaded from $final_p10k_url" chown "${USERNAME}:${USERNAME}" "${USER_HOME}/.p10k.zsh" else - echo "No custom .p10k.zsh URL provided. Skipping." + echo "Failed to download .p10k.zsh from $final_p10k_url" fi - # Always disable the Powerlevel10k wizard - echo "POWERLEVEL9K_DISABLE_CONFIGURATION_WIZARD=true" >>"${USER_HOME}/.zshrc" + # Disable Powerlevel10k wizard only if not already set + if ! grep -q 'POWERLEVEL9K_DISABLE_CONFIGURATION_WIZARD=true' "${USER_HOME}/.zshrc"; then + echo "Disabling Powerlevel10k config wizard" + echo "POWERLEVEL9K_DISABLE_CONFIGURATION_WIZARD=true" >>"${USER_HOME}/.zshrc" + fi } run_post_install_script() { @@ -201,10 +230,60 @@ run_post_install_script() { fi } +print_summary() { + echo + echo -e "${BOLD}> Setup Summary:${NC}" + echo + + summary_check() { + local label="$1" + local cmd="$2" + local version_cmd="$3" + printf " %-22s" "$label" + if command -v "$cmd" >/dev/null 2>&1; then + echo -en "${GREEN}✔ Installed${NC} " + { eval "$version_cmd" 2>/dev/null || echo "Version unknown"; } | head -n 1 || true + else + echo -e "${RED}✘ Not found${NC}" + fi + } + + [[ "$INSTALLZSH" == "true" ]] && summary_check "Zsh" zsh "zsh --version" + [[ "$OHMYZSH" == "true" ]] && summary_check "Oh My Zsh" zsh "grep -q oh-my-zsh ~/.zshrc && echo 'Enabled'" + [[ "$POWERLEVEL10K" == "true" ]] && summary_check "Powerlevel10k" zsh "grep -q powerlevel10k ~/.zshrc && echo 'Enabled'" + [[ "$NERDFONT" == "true" ]] && summary_check "Nerd Font" fc-cache "fc-list | grep -i meslo | head -n 1" + [[ "$AUTOSUGGESTIONS" == "true" ]] && summary_check "Autosuggestions" zsh "grep -q zsh-autosuggestions ~/.zshrc && echo 'Enabled'" + [[ "$SYNTAXHIGHLIGHTING" == "true" ]] && summary_check "Syntax Highlighting" zsh "grep -q zsh-syntax-highlighting ~/.zshrc && echo 'Enabled'" + [[ "$OPINIONATED" == "true" ]] && summary_check "Opinionated Config" curl "echo 'Custom .zshrc and .p10k.zsh used'" + + echo + echo -e "${BOLD}✔ Shell environment setup complete for ${USERNAME}!${NC}" +} + +configure_timezone() { + if [ -n "$TIMEZONE" ]; then + echo "Setting timezone to $TIMEZONE" + + ln -sf "/usr/share/zoneinfo/$TIMEZONE" /etc/localtime || { + echo "Failed to link timezone. Check if $TIMEZONE is valid." + return + } + echo "$TIMEZONE" >/etc/timezone 2>/dev/null || true + + # Debian-specific handling for tzdata reconfiguration + if command -v dpkg-reconfigure &>/dev/null; then + install_package_if_missing tzdata + DEBIAN_FRONTEND=noninteractive dpkg-reconfigure tzdata + fi + fi +} + # --- MAIN --- ensure_common_dependencies +configure_timezone + if [[ "${INSTALLZSH}" == "true" ]]; then install_zsh fi @@ -217,6 +296,10 @@ if [[ "${POWERLEVEL10K}" == "true" && "${OHMYZSH}" == "true" ]]; then install_powerlevel10k fi +if [[ "${NERDFONT}" == "true" ]]; then + install_nerd_font +fi + if [[ "${AUTOSUGGESTIONS}" == "true" && "${OHMYZSH}" == "true" ]]; then install_autosuggestions fi @@ -233,4 +316,4 @@ fix_permissions run_post_install_script -echo "Shell environment setup completed successfully for ${USERNAME}!" +print_summary From 3aeebe06706df748e1f38137d68114a8f52fd87e Mon Sep 17 00:00:00 2001 From: Jonatan Mata Date: Wed, 30 Apr 2025 13:24:38 -0600 Subject: [PATCH 2/2] feat: unified shell + sandbox setup with Copier, devcontainer CLI, and robust features Signed-off-by: Jonatan Mata --- LICENSE | 1 + README.md | 73 ++++++++++++++++------- SANDBOX.md | 92 +++++++++++++++++++++++++++++ src/shell/NOTES.md | 90 ++++++++++++++++++++++++++++ src/shell/README.md | 32 ---------- src/shell/devcontainer-feature.json | 4 +- 6 files changed, 237 insertions(+), 55 deletions(-) create mode 100644 SANDBOX.md create mode 100644 src/shell/NOTES.md delete mode 100644 src/shell/README.md diff --git a/LICENSE b/LICENSE index 35bef52..9d82670 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ MIT License Copyright (c) 2022 Microsoft Corporation +Copyright (c) 2025 Jonatan Mata Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 893eff2..33e1a09 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,23 @@ Modular, portable, and compatible with Amazon Linux, Ubuntu, and Debian-based sy ## Features Included -- **Shell Environment** (Zsh, Oh My Zsh, Powerlevel10k, syntax highlighting, autosuggestions) +- **Shell Environment** – Fully customizable Zsh setup with: + + - Zsh and Oh My Zsh + - Powerlevel10k theme + - Zsh autosuggestions and syntax highlighting + - Nerd Font v3 (Meslo) + - Timezone configuration + - Optional opinionated configuration + - Override support via `.zshrc` and `.p10k.zsh` URLs + - Post-install script hook + - **AWS CLI v2** - **Terraform** (with tfswitch) - **OpenTofu** (Terraform fork) - **Python** (via pyenv, with optional pipenv) - **Node.js** (via nvm) -- **Pre-commit** (Hooks Setup) +- **Pre-commit** (Hooks setup) Each tool is packaged as an independent, composable DevContainer Feature. @@ -23,20 +33,34 @@ You can reference features from this repository directly using the `gh:` prefix ```json { - "features": { - "gh:jonmatum/devcontainer-features/python:1.0.0": { - "version": "3.11.9", - "pipenv": true - }, - "gh:jonmatum/devcontainer-features/aws:1.0.0": {}, - "gh:jonmatum/devcontainer-features/terraform:1.0.0": {}, - "gh:jonmatum/devcontainer-features/shell:1.0.0": {} + "features": { + "gh:jonmatum/devcontainer-features/python:1.0.0": { + "version": "3.11.9", + "pipenv": true + }, + "gh:jonmatum/devcontainer-features/aws:1.0.0": {}, + "gh:jonmatum/devcontainer-features/terraform:1.0.0": {}, + "gh:jonmatum/devcontainer-features/shell:1.1.0": { + "timezone": "America/New_York", + "opinionated": true, + "zshrcUrl": "https://example.com/.zshrc", + "p10kUrl": "https://example.com/.p10k.zsh" } + } } ``` Replace the feature ID and version according to your requirements. +## Feature Highlights + +Each DevContainer Feature is: + +- Composable – Add only the tools you need +- Opinionated, but overridable – Defaults provided, but easy to customize +- Reusable – Designed for local dev, CI/CD, and cloud workspaces +- Tested – Verified against multiple base images and distros + ## Structure Each Feature is organized under the `src/` folder following the DevContainer [Feature distribution specification](https://containers.dev/implementors/features-distribution/). @@ -46,6 +70,7 @@ src/ shell/ devcontainer-feature.json install.sh + NOTES.md aws/ devcontainer-feature.json install.sh @@ -65,15 +90,13 @@ src/ ## Versioning -Each Feature is individually versioned using the `version` attribute in its `devcontainer-feature.json`. +Each Feature is individually versioned using the `version` attribute in its `devcontainer-feature.json`. Versioning follows [Semantic Versioning (SemVer)](https://semver.org/). -Example snippet from a Feature: - ```json { "id": "shell", - "version": "1.0.0", + "version": "1.1.0", ... } ``` @@ -82,23 +105,31 @@ Releases are automated via GitHub Actions using [release-please](https://github. ## Publishing -Features are automatically published to **GitHub Container Registry (GHCR)** following the [DevContainer Feature distribution spec](https://containers.dev/implementors/features-distribution/). +Features are automatically published to GitHub Container Registry (GHCR) following the [DevContainer Feature distribution spec](https://containers.dev/implementors/features-distribution/). - Each Feature is published individually under: `ghcr.io/jonmatum/devcontainer-features/:` -- A **feature collection metadata package** is also published: + +- A collection metadata package is also published: `ghcr.io/jonmatum/devcontainer-features` -> **Important:** After publishing, Features must be manually marked as **Public** in the GitHub Package settings to be discoverable and usable. +Important: After publishing, Features must be manually marked as **Public** in the GitHub Package settings to be discoverable and usable. **Example URLs:** -- Feature: `https://github.com/users/jonmatum/packages/container/devcontainer-features/shell` -- Collection: `https://github.com/users/jonmatum/packages/container/devcontainer-features` + +- Feature: [Shell Feature Package](https://github.com/users/jonmatum/packages/container/devcontainer-features/shell) +- Collection: [Feature Collection](https://github.com/users/jonmatum/packages/container/devcontainer-features) ## License -Licensed under the [MIT License](LICENSE). +This project is [MIT License](./LICENSE). +Originally scaffolded from Microsoft’s Dev Container Feature starter kit. +Extended and maintained by Jonatan Mata, 2025. + +## Additional Resources + +For advanced configuration, usage examples, and feature-specific notes, refer to the `NOTES.md` file within each feature’s folder. --- -> echo "Pura Vida & Happy Coding!"; +> echo "Pura Vida & Happy Coding!"; diff --git a/SANDBOX.md b/SANDBOX.md new file mode 100644 index 0000000..b10fa02 --- /dev/null +++ b/SANDBOX.md @@ -0,0 +1,92 @@ +# DevContainer Sandbox Automation + +This document outlines how to use the sandbox workflow powered by `Makefile` and [Copier](https://copier.readthedocs.io) to generate and manage a complete development environment based on DevContainer features. + +## Overview + +The sandbox system is designed for: + +- Rapid prototyping of feature combinations +- Reproducible DevContainer setups +- Simplified developer onboarding + +### Key Capabilities + +- Interactive CLI for selecting image + features +- Auto-generates `.copier-answers.yml` +- Renders `.sandbox/` workspace via Copier +- Builds and launches the container using the `devcontainer` CLI + +## Usage + +### 1. Interactive Setup + +```bash +make interactive +``` + +Prompts you to select an image and features, then writes a `.copier-answers.yml` file. + +### 2. Generate DevContainer + +```bash +make sandbox +``` + +Renders `.sandbox/` from `.template/` using Copier. +Copies selected features from `src/` into `.sandbox/.devcontainer/features/`. + +### 3. Launch Container + +```bash +make devcontainer-up +``` + +Uses the official `devcontainer` CLI to build and start the container. + +### 4. Run Commands + +```bash +make exec CMD="zsh" +``` + +Executes commands inside the container workspace. Defaults to `zsh`. + +## Files & Structure + +``` +. +├── .copier-answers.yml # Saved feature + image selection +├── .template/ # Copier template (jinja2-powered) +│ └── devcontainer.json.jinja +├── src/ # Feature source directories +├── .sandbox/ # Output devcontainer workspace +└── Makefile # Entrypoint with all tasks +``` + +## Make Targets + +| Command | Description | +| ---------------------- | -------------------------------------------- | +| `make help` | List all available commands | +| `make interactive` | Prompt to choose features + image | +| `make sandbox` | Render devcontainer into `.sandbox/` | +| `make devcontainer-up` | Build and start the container | +| `make exec` | Run command inside container (default `zsh`) | +| `make verify` | Check tool installation in container | +| `make validate` | Check `devcontainer.json` schema compliance | +| `make format` | Format the `devcontainer.json` via `jq` | + +## DevContainer CLI Requirement + +Make sure you have the [DevContainer CLI](https://containers.dev/implementors/cli/) installed (v0.76+): + +```bash +npm install -g @devcontainers/cli +``` + +## Advanced Notes + +- If Copier is not installed, the `Makefile` bootstraps it into a local virtualenv +- Answer files are reusable across environments and stored at `.copier-answers.yml` +- Additional validation and formatting tooling is built into the Make targets diff --git a/src/shell/NOTES.md b/src/shell/NOTES.md new file mode 100644 index 0000000..1e98487 --- /dev/null +++ b/src/shell/NOTES.md @@ -0,0 +1,90 @@ +# Devcontainer Shell Feature + +This repository provides a fully-automated shell environment for DevContainers using: + +- Zsh with Oh My Zsh, Powerlevel10k, and plugins +- Meslo Nerd Font v3 installer +- Cross-platform timezone support +- Customizable opinionated dotfiles +- Post-install script hook +- Smart feature summary at the end + +## Features Included + +### Shell Environment Options + +| Option | Type | Default | Description | +| ---------------------- | ------- | ------- | --------------------------------------------------------------------------------------- | +| `installZsh` | boolean | `true` | Install Zsh and set it as the default shell. | +| `ohMyZsh` | boolean | `true` | Install [Oh My Zsh](https://ohmyz.sh). | +| `powerlevel10k` | boolean | `true` | Install [Powerlevel10k](https://github.com/romkatv/powerlevel10k). | +| `autosuggestions` | boolean | `true` | Enable [zsh-autosuggestions](https://github.com/zsh-users/zsh-autosuggestions). | +| `syntaxHighlighting` | boolean | `true` | Enable [zsh-syntax-highlighting](https://github.com/zsh-users/zsh-syntax-highlighting). | +| `nerdFont` | boolean | `true` | Install Meslo Nerd Font v3.0.2 locally in user fonts. | +| `timezone` | string | `UTC` | Set system timezone (e.g. `Europe/Paris`). | +| `opinionated` | boolean | `false` | Apply curated `.zshrc` and `.p10k.zsh` configuration. | +| `zshrcUrl` | string | `""` | Custom `.zshrc` URL (overrides opinionated default). | +| `p10kUrl` | string | `""` | Custom `.p10k.zsh` URL (overrides opinionated default). | +| `postInstallScriptUrl` | string | `""` | Optional URL to a bash script to execute after setup. | + +## Meslo Nerd Font Installer + +Installs [Meslo Nerd Font v3.0.2](https://github.com/ryanoasis/nerd-fonts) into `~/.local/share/fonts`. + +- Supports Debian, Alpine, and Amazon Linux +- Verifies and installs required tools: `curl`, `unzip`, `fontconfig` +- Runs `fc-cache` to refresh fonts + +## Timezone Configuration + +If `timezone` is set: + +- Symlinks `/etc/localtime` to the correct zoneinfo file +- Writes to `/etc/timezone` +- Installs `tzdata` if necessary (Debian-based systems) + +Example: + +```json +"timezone": "America/Costa_Rica" +``` + +## Opinionated Dotfiles + +If `opinionated=true`, the following behavior is applied: + +- `.zshrc` and `.p10k.zsh` are downloaded from curated Gist URLs +- These can be overridden with `zshrcUrl` and `p10kUrl` +- Adds `POWERLEVEL9K_DISABLE_CONFIGURATION_WIZARD=true` to prevent interactive wizard + +## Post-Install Script Support + +A custom shell script can be executed automatically: + +```json +"postInstallScriptUrl": "https://example.com/my-extra-setup.sh" +``` + +This script will be downloaded and executed after the rest of the feature setup. + +## Final Setup Summary + +At the end of setup, a status table will be printed summarizing all enabled tools: + +``` +> Setup Summary: + + Zsh ✔ Installed zsh 5.9 + Oh My Zsh ✔ Installed Enabled + Powerlevel10k ✔ Installed Enabled + Nerd Font ✔ Installed MesloLGS NF + Autosuggestions ✔ Installed Enabled + Syntax Highlighting ✔ Installed Enabled + Opinionated Config ✔ Installed Custom .zshrc and .p10k.zsh used + +✔ Shell environment setup complete for user +``` + +--- + +> For sandbox and template automation via Make + Copier, see [`SANDBOX.md`](../../SANDBOX.md). diff --git a/src/shell/README.md b/src/shell/README.md deleted file mode 100644 index ef857fd..0000000 --- a/src/shell/README.md +++ /dev/null @@ -1,32 +0,0 @@ - -# Shell Setup (shell) - -Configurable shell setup with optional Zsh, Oh My Zsh, Powerlevel10k, autosuggestions, and syntax highlighting. - -## Example Usage - -```json -"features": { - "ghcr.io/jonmatum/devcontainer-features/shell:1": {} -} -``` - -## Options - -| Options Id | Description | Type | Default Value | -|-----|-----|-----|-----| -| installZsh | Install Zsh and set it as the default shell. | boolean | true | -| ohMyZsh | Install the Oh My Zsh framework. | boolean | true | -| powerlevel10k | Install the Powerlevel10k theme for Zsh. | boolean | true | -| autosuggestions | Enable the zsh-autosuggestions plugin. | boolean | true | -| syntaxHighlighting | Enable the zsh-syntax-highlighting plugin. | boolean | true | -| opinionated | Apply custom opinionated configuration. | boolean | false | -| zshrcUrl | URL to a custom .zshrc file. | string | - | -| p10kUrl | URL to a custom .p10k.zsh file. | string | - | -| postInstallScriptUrl | Optional URL to a bash script to execute at the end of the setup. | string | - | - - - ---- - -_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/jonmatum/devcontainer-features/blob/main/src/shell/devcontainer-feature.json). Add additional notes to a `NOTES.md`._ diff --git a/src/shell/devcontainer-feature.json b/src/shell/devcontainer-feature.json index 8a4018a..fa8707c 100644 --- a/src/shell/devcontainer-feature.json +++ b/src/shell/devcontainer-feature.json @@ -1,6 +1,6 @@ { "id": "shell", - "version": "1.1.2", + "version": "1.2.0", "name": "Shell Setup", "description": "Configurable shell setup with optional Zsh, Oh My Zsh, Powerlevel10k, autosuggestions, and syntax highlighting.", "options": { @@ -60,4 +60,4 @@ "description": "Optional URL to a bash script to execute at the end of the setup." } } -} \ No newline at end of file +}