diff --git a/README.md b/README.md index 63d16ec..f754b2c 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ This repository contains a _collection_ of Features. | Codex-cli | https://github.com/openai/codex | Codex CLI is an experimental project under active development. | | ccc | https://github.com/jsburckhardt/co-config | A TUI tool to interactively configure and view GitHub Copilot CLI settings. | | Yazi | https://github.com/sxyazi/yazi | Blazing fast terminal file manager written in Rust, based on async I/O. | +| Copilot Persistence | — | Preserve ~/.copilot folder across container instances (avoids losing Copilot configuration after rebuilding). | @@ -373,3 +374,21 @@ Running `yazi --version` inside the built container will print the version of ya ```bash yazi --version ``` + +### `copilot-persistence` + +Persists the `~/.copilot` directory across container rebuilds using a Docker volume and symlink, so you don't lose your Copilot configuration when rebuilding your dev container. + +```jsonc +{ + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "ghcr.io/jsburckhardt/devcontainer-features/copilot-persistence:1": {} + } +} +``` + +```bash +# Verify the symlink exists +test -L "$HOME/.copilot" && echo "copilot-persistence is active" +``` diff --git a/src/copilot-persistence/devcontainer-feature.json b/src/copilot-persistence/devcontainer-feature.json new file mode 100644 index 0000000..7996172 --- /dev/null +++ b/src/copilot-persistence/devcontainer-feature.json @@ -0,0 +1,17 @@ +{ + "name": "Copilot Persistence", + "id": "copilot-persistence", + "version": "1.0.0", + "description": "Preserve ~/.copilot folder across container instances (avoids losing Copilot configuration after rebuilding)", + "options": {}, + "mounts": [ + { + "source": "${devcontainerId}-copilot", + "target": "/dc/copilot", + "type": "volume" + } + ], + "onCreateCommand": { + "copilot-persistence": "/usr/local/share/jsburckhardt-devcontainer-features/copilot-persistence/scripts/oncreate.sh" + } +} diff --git a/src/copilot-persistence/install.sh b/src/copilot-persistence/install.sh new file mode 100644 index 0000000..89eb977 --- /dev/null +++ b/src/copilot-persistence/install.sh @@ -0,0 +1,49 @@ +#!/bin/sh +set -e + +FEATURE_DIR="/usr/local/share/jsburckhardt-devcontainer-features/copilot-persistence" +LIFECYCLE_SCRIPTS_DIR="$FEATURE_DIR/scripts" +LOG_FILE="$FEATURE_DIR/log.txt" + +mkdir -p "${FEATURE_DIR}" +echo "" > "$LOG_FILE" + +log() { + echo "$1" + echo "$1" >> "$LOG_FILE" +} + +log "Activating feature 'copilot-persistence'" +log "User: ${_REMOTE_USER} User home: ${_REMOTE_USER_HOME}" + +# Skip if already installed +if [ -f "$FEATURE_DIR/markers/install" ]; then + log "Feature 'copilot-persistence' already installed, skipping" + exit 0 +fi + +# Back up existing .copilot folder if present +got_old_copilot_folder=false +if [ -e "$_REMOTE_USER_HOME/.copilot" ]; then + log "Moving existing .copilot folder to .copilot-old" + mv "$_REMOTE_USER_HOME/.copilot" "$_REMOTE_USER_HOME/.copilot-old" + got_old_copilot_folder=true +else + log "No existing .copilot folder found at '$_REMOTE_USER_HOME/.copilot'" +fi + +# Create symlink from ~/.copilot to the mounted volume +ln -s /dc/copilot/ "$_REMOTE_USER_HOME/.copilot" +chown -R "${_REMOTE_USER}:${_REMOTE_USER}" "$_REMOTE_USER_HOME/.copilot" + +# Copy lifecycle scripts +if [ -f oncreate.sh ]; then + mkdir -p "${LIFECYCLE_SCRIPTS_DIR}" + cp oncreate.sh "${LIFECYCLE_SCRIPTS_DIR}/oncreate.sh" + chmod +x "${LIFECYCLE_SCRIPTS_DIR}/oncreate.sh" +fi + +# Mark as installed +log "Adding marker file to indicate persistence is installed" +mkdir -p "$FEATURE_DIR/markers" +touch "$FEATURE_DIR/markers/install" diff --git a/src/copilot-persistence/oncreate.sh b/src/copilot-persistence/oncreate.sh new file mode 100644 index 0000000..90bcad9 --- /dev/null +++ b/src/copilot-persistence/oncreate.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +set -e + +FEATURE_DIR="/usr/local/share/jsburckhardt-devcontainer-features/copilot-persistence" +LOG_FILE="$FEATURE_DIR/log.txt" + +log() { + echo "$1" + echo "$1" >> "$LOG_FILE" +} + +# Fix permissions on the log file so the remote user can write to it +if command -v sudo > /dev/null; then + sudo chown -R "$(id -u):$(id -g)" "$LOG_FILE" +else + chown -R "$(id -u):$(id -g)" "$LOG_FILE" +fi + +log "In OnCreate script" + +# Skip if oncreate actions already ran +if [ -f "$FEATURE_DIR/markers/oncreate" ]; then + log "Feature 'copilot-persistence' oncreate actions already run, skipping" + exit 0 +fi + +fix_permissions() { + local dir="${1}" + if [ ! -w "${dir}" ]; then + echo "Fixing permissions of '${dir}'..." + sudo chown -R "$(id -u):$(id -g)" "${dir}" + echo "Done!" + else + echo "Permissions of '${dir}' are OK!" + fi +} + +fix_permissions "/dc/copilot" + +# Mark oncreate as done +log "Adding marker file to indicate oncreate actions have been run" +mkdir -p "$FEATURE_DIR/markers" +if command -v sudo > /dev/null; then + sudo touch "$FEATURE_DIR/markers/oncreate" +else + touch "$FEATURE_DIR/markers/oncreate" +fi + +log "Done" diff --git a/test/_global/scenarios.json b/test/_global/scenarios.json index 33acde0..c1727ad 100644 --- a/test/_global/scenarios.json +++ b/test/_global/scenarios.json @@ -165,5 +165,11 @@ "version": "v26.1.22" } } + }, + "copilot-persistence": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "copilot-persistence": {} + } } } diff --git a/test/copilot-persistence/test.sh b/test/copilot-persistence/test.sh new file mode 100644 index 0000000..0bb419f --- /dev/null +++ b/test/copilot-persistence/test.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -e + +source dev-container-features-test-lib +check "copilot-persistence" test -L "$HOME/.copilot" +reportResults