From 13a9fa26cdfd942dcdda69725861f6b8b72bb945 Mon Sep 17 00:00:00 2001 From: Nebula <40148908+nebula-it@users.noreply.github.com> Date: Mon, 10 Aug 2026 21:07:52 -0700 Subject: [PATCH 1/6] feat: Add support for preserving pwsh history --- src/powershell/devcontainer-feature.json | 17 ++++++++++++++++- src/powershell/install.sh | 19 +++++++++++++++++++ src/powershell/oncreate.sh | 16 ++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/powershell/oncreate.sh diff --git a/src/powershell/devcontainer-feature.json b/src/powershell/devcontainer-feature.json index 009f4c238..c39e4efac 100644 --- a/src/powershell/devcontainer-feature.json +++ b/src/powershell/devcontainer-feature.json @@ -2,7 +2,7 @@ "id": "powershell", "version": "2.0.2", "name": "PowerShell", - "documentationURL": "https://github.com/devcontainers/features/tree/main/src/powershell", + "documentationURL": "https://github.com/devcontainers/features/tree/main/src/powershell2", "description": "Installs PowerShell along with needed dependencies. Useful for base Dockerfiles that often are missing required install dependencies like gpg.", "options": { "version": { @@ -28,6 +28,11 @@ "type": "string", "default": "", "description": "Optional (publicly accessible) URL to download PowerShell profile." + }, + "preserveHistory": { + "type": "boolean", + "default": true, + "description": "Optional Enable to preserve the powershell history across container restart/rebuilds" } }, "customizations": { @@ -44,6 +49,16 @@ ] } }, + "mounts": [ + { + "type": "volume", + "source": "${devcontainerId}-powershell-history", + "target": "/dc/powershell" + } + ], + "onCreateCommand": { + "powershell": "/usr/local/share/powershell/scripts/oncreate.sh" + }, "installsAfter": [ "ghcr.io/devcontainers/features/common-utils" ] diff --git a/src/powershell/install.sh b/src/powershell/install.sh index cabd2cbb4..1dd60b3fc 100755 --- a/src/powershell/install.sh +++ b/src/powershell/install.sh @@ -9,12 +9,16 @@ set -e +# Capture the location of this script once so later directory changes do not affect relative file lookups. +SOURCE_DIR="$(pwd)" + # Clean up rm -rf /var/lib/apt/lists/* POWERSHELL_VERSION=${VERSION:-"latest"} POWERSHELL_MODULES="${MODULES:-""}" POWERSHELL_PROFILE_URL="${POWERSHELLPROFILEURL}" +PRESERVE_HISTORY="${PRESERVEHISTORY}" MICROSOFT_GPG_KEYS_URI="https://packages.microsoft.com/keys/microsoft.asc" #MICROSOFT_GPG_KEYS_URI=$(curl https://packages.microsoft.com/keys/microsoft.asc -o /usr/share/keyrings/microsoft-archive-keyring.gpg) @@ -497,6 +501,21 @@ if [ -n "$POWERSHELL_PROFILE_URL" ]; then sudo -E curl -sSL -o "$profilePath" "$POWERSHELL_PROFILE_URL" fi +# Copy persistent history setup script +ONCREATE_SOURCE="${SOURCE_DIR}/oncreate.sh" +SCRIPTS_DIR="/usr/local/share/powershell/scripts" +mkdir -p "$SCRIPTS_DIR" +echo "Checking oncreate.sh" +if [ ! -f "$ONCREATE_SOURCE" ]; then + echo "oncreate.sh not found at expected path: $ONCREATE_SOURCE" >&2 + exit 1 +fi +if [ -f "$ONCREATE_SOURCE" ]; then + cp "$ONCREATE_SOURCE" "$SCRIPTS_DIR/oncreate.sh" + sed -i "s|preserveHistoryFromInstallSh|${PRESERVE_HISTORY}|" "$SCRIPTS_DIR/oncreate.sh" + chmod +x "$SCRIPTS_DIR/oncreate.sh" +fi + # Clean up rm -rf /var/lib/apt/lists/* diff --git a/src/powershell/oncreate.sh b/src/powershell/oncreate.sh new file mode 100644 index 000000000..0ccc255e5 --- /dev/null +++ b/src/powershell/oncreate.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -e + +PRESERVE_HISTORY="preserveHistoryFromInstallSh" +HISTORY_SAVE_LINE='Set-PSReadLineOption -HistorySavePath /dc/powershell/history.txt' + +if [ "${PRESERVE_HISTORY}" = "true" ]; then + sudo chown -R "$(id -u):$(id -g)" "/dc/powershell" + profile_path="$(pwsh -NoProfile -Command '$PROFILE')" + mkdir -p "$(dirname "$profile_path")" + touch "$profile_path" + + if ! grep -Fxq "$HISTORY_SAVE_LINE" "$profile_path"; then + printf '\n%s\n' "$HISTORY_SAVE_LINE" >> "$profile_path" + fi +fi From 264e8bc1a107c7f33451d0e844159bb4606fe359 Mon Sep 17 00:00:00 2001 From: Nebula <40148908+nebula-it@users.noreply.github.com> Date: Mon, 10 Aug 2026 21:34:20 -0700 Subject: [PATCH 2/6] Remove typo --- src/powershell/devcontainer-feature.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/powershell/devcontainer-feature.json b/src/powershell/devcontainer-feature.json index c39e4efac..aac925d78 100644 --- a/src/powershell/devcontainer-feature.json +++ b/src/powershell/devcontainer-feature.json @@ -2,7 +2,7 @@ "id": "powershell", "version": "2.0.2", "name": "PowerShell", - "documentationURL": "https://github.com/devcontainers/features/tree/main/src/powershell2", + "documentationURL": "https://github.com/devcontainers/features/tree/main/src/powershell", "description": "Installs PowerShell along with needed dependencies. Useful for base Dockerfiles that often are missing required install dependencies like gpg.", "options": { "version": { From 9f568218ff194aeda4a3615e96f7bc7eea60a22b Mon Sep 17 00:00:00 2001 From: Nebula <40148908+nebula-it@users.noreply.github.com> Date: Mon, 10 Aug 2026 21:35:51 -0700 Subject: [PATCH 3/6] Bump feature version --- src/powershell/devcontainer-feature.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/powershell/devcontainer-feature.json b/src/powershell/devcontainer-feature.json index aac925d78..906d4bbcf 100644 --- a/src/powershell/devcontainer-feature.json +++ b/src/powershell/devcontainer-feature.json @@ -1,6 +1,6 @@ { "id": "powershell", - "version": "2.0.2", + "version": "2.0.3", "name": "PowerShell", "documentationURL": "https://github.com/devcontainers/features/tree/main/src/powershell", "description": "Installs PowerShell along with needed dependencies. Useful for base Dockerfiles that often are missing required install dependencies like gpg.", From 8043092c4c3b28e9bfdba3cca0e254dac7b48abd Mon Sep 17 00:00:00 2001 From: Nebula <40148908+nebula-it@users.noreply.github.com> Date: Mon, 10 Aug 2026 21:38:21 -0700 Subject: [PATCH 4/6] Update docs --- src/powershell/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/powershell/README.md b/src/powershell/README.md index f09ac1f02..59a391a2b 100644 --- a/src/powershell/README.md +++ b/src/powershell/README.md @@ -18,6 +18,7 @@ Installs PowerShell along with needed dependencies. Useful for base Dockerfiles | version | Select or enter a version of PowerShell. | string | latest | | modules | Optional comma separated list of PowerShell modules to install. If you need to install a specific version of a module, use '==' to specify the version (e.g. 'az.resources==2.5.0') | string | - | | powershellProfileURL | Optional (publicly accessible) URL to download PowerShell profile. | string | - | +| preserveHistory | Optional Enable to preserve the powershell history across container restart/rebuilds | boolean | true | ## Customizations From f28388619fa137708ade5e51e43be0f3f5335ac4 Mon Sep 17 00:00:00 2001 From: Nebula <40148908+nebula-it@users.noreply.github.com> Date: Fri, 14 Aug 2026 16:58:29 -0700 Subject: [PATCH 5/6] Handle cases when `sudo` is not available --- src/powershell/oncreate.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/powershell/oncreate.sh b/src/powershell/oncreate.sh index 0ccc255e5..c9f6f5a0a 100644 --- a/src/powershell/oncreate.sh +++ b/src/powershell/oncreate.sh @@ -5,7 +5,15 @@ PRESERVE_HISTORY="preserveHistoryFromInstallSh" HISTORY_SAVE_LINE='Set-PSReadLineOption -HistorySavePath /dc/powershell/history.txt' if [ "${PRESERVE_HISTORY}" = "true" ]; then - sudo chown -R "$(id -u):$(id -g)" "/dc/powershell" + if [ "$(stat -c '%u:%g' "/dc/powershell")" = "$(id -u):$(id -g)" ]; then + echo "Already owned by the current user, nothing to do" + elif [ "$(id -u)" -eq 0 ]; then + chown -R "$(id -u):$(id -g)" "/dc/powershell" + elif command -v sudo >/dev/null 2>&1; then + sudo chown -R "$(id -u):$(id -g)" "/dc/powershell" + else + echo "Warning: unable to chown /dc/powershell (current user not root and no sudo available); history may not persist." >&2 + fi profile_path="$(pwsh -NoProfile -Command '$PROFILE')" mkdir -p "$(dirname "$profile_path")" touch "$profile_path" From f3adecb26814067d41f225c4b1de5a87b3716077 Mon Sep 17 00:00:00 2001 From: Nebula <40148908+nebula-it@users.noreply.github.com> Date: Fri, 14 Aug 2026 16:59:49 -0700 Subject: [PATCH 6/6] Improve the profile download logic so if curl is not able to retreive the file, it does not write error message into $PROFILE. Remove redundant `sudo` since this script is required to run as root --- src/powershell/install.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/powershell/install.sh b/src/powershell/install.sh index 1dd60b3fc..942bf6d8e 100755 --- a/src/powershell/install.sh +++ b/src/powershell/install.sh @@ -498,7 +498,15 @@ if [ -n "$POWERSHELL_PROFILE_URL" ]; then echo "Downloading PowerShell Profile from: $POWERSHELL_PROFILE_URL" # Get profile path from currently installed pwsh profilePath=$(pwsh -noni -c '$PROFILE.AllUsersAllHosts') - sudo -E curl -sSL -o "$profilePath" "$POWERSHELL_PROFILE_URL" + # Download to a temp file first so a non-200 response never overwrites the profile + tmpProfile="$(mktemp)" + http_status=$(curl -sSL -w '%{http_code}' -o "$tmpProfile" "$POWERSHELL_PROFILE_URL") + if [ "$http_status" = "200" ]; then + mv "$tmpProfile" "$profilePath" + else + echo "Failed to download PowerShell profile (HTTP ${http_status}). Skipping profile update." >&2 + rm -f "$tmpProfile" + fi fi # Copy persistent history setup script