Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
14c84ab
feat(shell): add new feature and tests for default and minimal scenarios
jonmatum Apr 26, 2025
bea61ba
fix(shell): update feature file name
jonmatum Apr 26, 2025
bca3c60
fix(shell): polish devcontainer-feature.json metadata and descriptions
jonmatum Apr 26, 2025
eee87b3
test: add minimal shell scenario validation
jonmatum Apr 27, 2025
e3514d3
chore: improve automated documentation PR to follow conventional commits
jonmatum Apr 27, 2025
77181fc
test(shell): add validation for minimal zsh setup without oh-my-zsh
jonmatum Apr 27, 2025
30b0fa6
test(ci): add Amazon Linux 2023 to base image matrix for feature testing
jonmatum Apr 27, 2025
b277ef5
fix(shell): dynamically install git before shell setup to ensure full…
jonmatum Apr 27, 2025
90b57f8
fix(shell): ensure .zshrc exists before updating it to prevent missin…
jonmatum Apr 27, 2025
23f1e9e
test(shell): ensure .zshrc exists before updating it to prevent missi…
jonmatum Apr 27, 2025
85ea32a
fix(shell): correctly handle root user's home directory
jonmatum Apr 27, 2025
3644a0a
fix(shell): safely source feature-utils.sh if available
jonmatum Apr 27, 2025
359af56
fix(shell): ensure plugins and themes install only if Oh My Zsh is en…
jonmatum Apr 27, 2025
ef5cd9e
feat(shell): only install oh-my-zsh and plugins if zsh is installed
jonmatum Apr 27, 2025
8d21c5a
feat(shell): finalize robust Zsh and Oh My Zsh setup
jonmatum Apr 27, 2025
fcaeb51
test(shell): install
jonmatum Apr 27, 2025
d7b73b4
test(scenarios): add scenarios for amazonlinux, debian, ubuntu base i…
jonmatum Apr 27, 2025
e133792
test(scenarios): add scenarios for amazonlinux, debian, ubuntu base i…
jonmatum Apr 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,23 @@ jobs:
run: |
set -e
echo "Start."

# Configure git and Push updates
git config --global user.email github-actions[bot]@users.noreply.github.com
git config --global user.name github-actions[bot]
git config pull.rebase false
branch=automated-documentation-update-$GITHUB_RUN_ID
git checkout -b $branch
message='Automated documentation update'

branch=automated/documentation-update-$GITHUB_RUN_ID
git checkout -b "$branch"

# Conventional commit message
message='docs: update feature documentation [skip ci]'

# Add / update and commit
git add */**/README.md
git commit -m 'Automated documentation update [skip ci]' || export NO_UPDATES=true
# Push
git commit -m "$message" || export NO_UPDATES=true

# Push and create PR
if [ "$NO_UPDATES" != "true" ] ; then
git push origin "$branch"
gh pr create --title "$message" --body "$message"
Expand Down
7 changes: 3 additions & 4 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ jobs:
strategy:
matrix:
features:
- color
- hello
- shell
baseImage:
- debian:latest
- ubuntu:latest
- mcr.microsoft.com/devcontainers/base:ubuntu
- amazonlinux:2023
steps:
- uses: actions/checkout@v4

Expand All @@ -34,8 +34,7 @@ jobs:
strategy:
matrix:
features:
- color
- hello
- shell
steps:
- uses: actions/checkout@v4

Expand Down
9 changes: 9 additions & 0 deletions .sandbox/.devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "Feature Test Sandbox",
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"features": {
"./features/shell": {
"opinionated": true
}
}
}
1,713 changes: 1,713 additions & 0 deletions .sandbox/.devcontainer/features/shell/assets/p10k.zsh

Large diffs are not rendered by default.

42 changes: 42 additions & 0 deletions .sandbox/.devcontainer/features/shell/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"id": "shell",
"version": "0.0.0",
"name": "Shell Environment",
"description": "Configurable shell setup with optional Zsh, Oh My Zsh, Powerlevel10k, autosuggestions, and syntax highlighting.",
"options": {
"installZsh": {
"type": "boolean",
"default": true,
"description": "Install Zsh and set it as the default shell."
},
"ohMyZsh": {
"type": "boolean",
"default": true,
"description": "Install the Oh My Zsh framework."
},
"powerlevel10k": {
"type": "boolean",
"default": true,
"description": "Install the Powerlevel10k theme for Zsh."
},
"autosuggestions": {
"type": "boolean",
"default": true,
"description": "Enable the zsh-autosuggestions plugin."
},
"syntaxHighlighting": {
"type": "boolean",
"default": true,
"description": "Enable the zsh-syntax-highlighting plugin."
},
"opinionated": {
"type": "boolean",
"default": false,
"description": "Apply an opinionated Powerlevel10k configuration for a highly customized prompt."
}
},
"entrypoint": "install.sh",
"installsAfter": [
"ghcr.io/devcontainers/features/common-utils"
]
}
218 changes: 218 additions & 0 deletions .sandbox/.devcontainer/features/shell/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
#!/bin/bash
set -e

# Load feature-utils if available
if [ -f "/usr/local/share/feature-utils.sh" ]; then
. /usr/local/share/feature-utils.sh
else
echo "Warning: feature-utils.sh not found. Continuing without it."
fi

echo "> Starting shell environment setup..."

USERNAME="${_REMOTE_USER:-vscode}"

# Resolve home directory safely
if [ "${USERNAME}" = "root" ]; then
USER_HOME="/root"
elif [ -d "/home/${USERNAME}" ]; then
USER_HOME="/home/${USERNAME}"
else
echo "Warning: User home for '${USERNAME}' not found. Defaulting to /root."
USER_HOME="/root"
fi

ZSHRC="${USER_HOME}/.zshrc"
OMZ_DIR="${USER_HOME}/.oh-my-zsh"
ZSH_CUSTOM="${OMZ_DIR}/custom"

# Feature options
: "${installZsh:=true}"
: "${ohMyZsh:=true}"
: "${powerlevel10k:=true}"
: "${autosuggestions:=true}"
: "${syntaxHighlighting:=true}"
: "${opinionated:=false}"
: "${autosuggestHighlight:=fg=8}"

detect_package_manager() {
if command -v apt-get &>/dev/null; then
echo "apt"
elif command -v dnf &>/dev/null; then
echo "dnf"
elif command -v yum &>/dev/null; then
echo "yum"
elif command -v apk &>/dev/null; then
echo "apk"
else
echo "unsupported"
fi
}

install_package_if_missing() {
local package="$1"
if ! command -v "$package" &>/dev/null; then
echo "Installing missing package: $package"
local pm
pm=$(detect_package_manager)
case "${pm}" in
apt)
apt-get update -y && apt-get install -y --no-install-recommends "$package"
;;
dnf)
dnf install -y --allowerasing "$package"
;;
yum)
yum install -y "$package"
;;
apk)
apk add --no-cache "$package"
;;
*)
echo "Unsupported package manager."
exit 1
;;
esac
fi
}

ensure_common_dependencies() {
echo "Ensuring common system dependencies..."
for pkg in curl git tar bash ca-certificates; do
install_package_if_missing "$pkg"
done
}

install_zsh() {
if ! command -v zsh &>/dev/null; then
install_package_if_missing zsh
# Special case: Amazon Linux needs util-linux-user for chsh
local pm
pm=$(detect_package_manager)
if [[ "$pm" == "dnf" || "$pm" == "yum" ]]; then
install_package_if_missing util-linux-user || echo "Skipping util-linux-user: not needed."
fi
fi

echo "Changing default shell to Zsh for user ${USERNAME}"
chsh -s "$(command -v zsh)" "${USERNAME}" || echo "Warning: Failed to change shell, continuing..."
}

install_oh_my_zsh() {
if [ ! -d "${OMZ_DIR}" ]; then
echo "Installing Oh My Zsh for ${USERNAME}..."
# Set correct HOME manually so installer puts files in the right place
HOME="${USER_HOME}" su - "${USERNAME}" -c "sh -c \"\$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)\" --unattended"

# Ensure correct ownership
chown -R "${USERNAME}:${USERNAME}" "${OMZ_DIR}"
else
echo "Oh My Zsh already installed, skipping."
fi

# Make sure .zshrc points to correct OMZ install
if [ ! -f "${ZSHRC}" ]; then
echo "Creating missing .zshrc at ${ZSHRC}"
echo 'export ZSH="$HOME/.oh-my-zsh"' >"${ZSHRC}"
echo 'ZSH_THEME="robbyrussell"' >>"${ZSHRC}"
chown "${USERNAME}:${USERNAME}" "${ZSHRC}"
else
grep -qxF 'export ZSH="$HOME/.oh-my-zsh"' "${ZSHRC}" || echo 'export ZSH="$HOME/.oh-my-zsh"' >>"${ZSHRC}"
grep -qxF 'ZSH_THEME="robbyrussell"' "${ZSHRC}" || echo 'ZSH_THEME="robbyrussell"' >>"${ZSHRC}"
fi
}

install_powerlevel10k() {
local theme_dir="${ZSH_CUSTOM}/themes/powerlevel10k"

if [ ! -d "${theme_dir}" ]; then
echo "Installing Powerlevel10k..."
su - "${USERNAME}" -c "git clone --depth=1 https://github.com/romkatv/powerlevel10k.git '${theme_dir}'"
else
echo "Powerlevel10k already installed, skipping."
fi

# Ensure theme activation
if grep -q '^ZSH_THEME=' "${ZSHRC}"; then
sed -i 's/^ZSH_THEME=.*/ZSH_THEME="powerlevel10k\/powerlevel10k"/' "${ZSHRC}"
else
echo 'ZSH_THEME="powerlevel10k/powerlevel10k"' >>"${ZSHRC}"
fi
}

install_autosuggestions() {
local plugin_dir="${ZSH_CUSTOM}/plugins/zsh-autosuggestions"
if [ ! -d "${plugin_dir}" ]; then
echo "Installing zsh-autosuggestions plugin..."
su - "${USERNAME}" -c "git clone https://github.com/zsh-users/zsh-autosuggestions '${plugin_dir}'"
fi
grep -qxF "source ${plugin_dir}/zsh-autosuggestions.zsh" "${ZSHRC}" || echo "source ${plugin_dir}/zsh-autosuggestions.zsh" >>"${ZSHRC}"
grep -qxF "ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='${autosuggestHighlight}'" "${ZSHRC}" || echo "ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='${autosuggestHighlight}'" >>"${ZSHRC}"
}

install_syntax_highlighting() {
local plugin_dir="${ZSH_CUSTOM}/plugins/zsh-syntax-highlighting"
if [ ! -d "${plugin_dir}" ]; then
echo "Installing zsh-syntax-highlighting plugin..."
su - "${USERNAME}" -c "git clone https://github.com/zsh-users/zsh-syntax-highlighting.git '${plugin_dir}'"
fi
grep -qxF "source ${plugin_dir}/zsh-syntax-highlighting.zsh" "${ZSHRC}" || echo "source ${plugin_dir}/zsh-syntax-highlighting.zsh" >>"${ZSHRC}"
}

fix_permissions() {
echo "Fixing permissions..."
chown -R "${USERNAME}:${USERNAME}" "${USER_HOME}"
}

apply_opinionated_files() {
echo "Applying opinionated config files..."

local assets_dir
assets_dir="$(dirname "$0")/assets"

if [ -d "$assets_dir" ]; then
for file in "$assets_dir"/*; do
base_name="$(basename "$file")"
target_name="${base_name}"
# If the file doesn't start with '.', add one (make it hidden)
[[ "${base_name}" != .* ]] && target_name=".${base_name}"
echo "Copying ${file} to ${USER_HOME}/${target_name}"
cp "$file" "${USER_HOME}/${target_name}"
chown "${USERNAME}:${USERNAME}" "${USER_HOME}/${target_name}"
done
else
echo "Warning: Assets directory not found: $assets_dir"
fi
}

# --- MAIN ---

ensure_common_dependencies

if [[ "${installZsh}" == "true" ]]; then
install_zsh
fi

if [[ "${installZsh}" == "true" && "${ohMyZsh}" == "true" ]]; then
install_oh_my_zsh
fi

if [[ "${powerlevel10k}" == "true" && "${ohMyZsh}" == "true" ]]; then
install_powerlevel10k
fi

if [[ "${autosuggestions}" == "true" && "${ohMyZsh}" == "true" ]]; then
install_autosuggestions
fi

if [[ "${syntaxHighlighting}" == "true" && "${ohMyZsh}" == "true" ]]; then
install_syntax_highlighting
fi

fix_permissions

if [[ "${opinionated}" == "true" ]]; then
apply_opinionated_files
fi

echo "Shell environment setup completed successfully for ${USERNAME}!"
Loading