microsoft/TypeAgent

Public

mirrored fromhttps://github.com/microsoft/TypeAgentAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
container

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

.devcontainer/scripts/post-create.sh

275lines · modecode

1#!/bin/bash
2# Copyright (c) Microsoft Corporation.
3# Licensed under the MIT License.
4
5#
6# TypeAgent DevContainer Post-Create Script
7# Runs once when the container is first created
8#
9
10set -euo pipefail
11
12echo "╔══════════════════════════════════════════════════════════════╗"
13echo "║ TypeAgent DevContainer Setup ║"
14echo "╚══════════════════════════════════════════════════════════════╝"
15echo ""
16
17# Detect environment
18detect_env() {
19 if [[ "${CODESPACES:-}" == "true" ]]; then
20 echo "codespaces"
21 elif [[ -n "${WSL_DISTRO_NAME:-}" ]] || grep -qi "wsl" /proc/version 2>/dev/null; then
22 if [[ -n "${WAYLAND_DISPLAY:-}" ]] || [[ -n "${DISPLAY:-}" ]]; then
23 echo "wsl2-gui"
24 else
25 echo "wsl2"
26 fi
27 else
28 echo "standard"
29 fi
30}
31
32ENV=$(detect_env)
33echo "Environment: $ENV"
34echo ""
35
36# Ensure worktree roots are writable for agent windows.
37echo "Preparing worktree roots for agent windows..."
38WORKSPACE_DIR=$(pwd -P)
39WORKTREES_DIR="${WORKSPACE_DIR}.worktrees"
40for dir in "$WORKTREES_DIR"; do
41 if [[ ! -d "$dir" ]]; then
42 if sudo mkdir -p "$dir"; then
43 echo " created $dir"
44 else
45 echo " warn: could not create $dir"
46 continue
47 fi
48 fi
49
50 if sudo chown codespace:codespace "$dir"; then
51 echo " $dir owned by codespace"
52 else
53 echo " warn: could not set ownership for $dir"
54 fi
55done
56echo ""
57
58# Fix ownership of Docker named-volume mount points.
59# Named volumes mounted into the container are owned by root:root by default,
60# which prevents the non-root `codespace` user from writing into them
61# (e.g. `pnpm install` -> EACCES on ts/node_modules).
62echo "Fixing ownership of mounted volume directories..."
63VOLUME_PATHS=(
64 "/home/codespace/.local/share/pnpm"
65 "/home/codespace/.local/share/pnpm/store"
66 "/home/codespace/.claude"
67)
68# Discover the workspace ts/node_modules path dynamically (works for worktrees too)
69WS_TS_DIR=""
70if [[ -d "/workspaces/TypeAgent/ts" ]]; then
71 WS_TS_DIR="/workspaces/TypeAgent/ts"
72else
73 WS_TS_DIR=$(find /workspaces -maxdepth 2 -type d -name "ts" 2>/dev/null | head -1)
74fi
75if [[ -n "$WS_TS_DIR" ]]; then
76 VOLUME_PATHS+=("$WS_TS_DIR/node_modules")
77fi
78
79for p in "${VOLUME_PATHS[@]}"; do
80 if [[ -e "$p" ]]; then
81 if sudo chown -R codespace:codespace "$p"; then
82 echo " chowned $p"
83 else
84 if [[ "$p" == *"/pnpm/store" ]] || [[ "$p" == *"/node_modules" ]]; then
85 echo "Error: failed to chown critical path $p" >&2
86 exit 1
87 fi
88 echo " warn: could not chown $p"
89 fi
90 fi
91done
92echo ""
93
94# Navigate to TypeScript workspace
95echo "Looking for TypeScript workspace..."
96REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || true)
97if [[ -n "$REPO_ROOT" ]] && [[ -d "$REPO_ROOT/ts" ]]; then
98 cd "$REPO_ROOT/ts"
99 echo "Found: $REPO_ROOT/ts"
100else
101 # Try glob pattern
102 TS_DIR=$(find /workspaces -maxdepth 2 -type d -name "ts" 2>/dev/null | head -1)
103 if [[ -n "$TS_DIR" ]]; then
104 cd "$TS_DIR"
105 echo "Found: $TS_DIR"
106 else
107 echo "Warning: Could not find ts directory in /workspaces"
108 echo "Listing /workspaces contents:"
109 ls -la /workspaces/ 2>/dev/null || echo " /workspaces not accessible"
110 echo ""
111 echo "Skipping dependency installation. Run manually after container starts:"
112 echo " cd ts && pnpm install"
113 exit 0
114 fi
115fi
116
117# Enable pnpm
118echo ""
119echo "Enabling corepack and pnpm..."
120if command -v corepack &> /dev/null; then
121 corepack enable || echo "Warning: corepack enable failed"
122 # Use the pnpm version pinned in package.json (packageManager field)
123 corepack install || echo "Warning: corepack install failed"
124else
125 echo "Warning: corepack not found, checking for pnpm..."
126 if ! command -v pnpm &> /dev/null; then
127 echo "Installing pnpm via npm..."
128 npm install -g pnpm || { echo "Failed to install pnpm"; exit 1; }
129 fi
130fi
131
132# Verify pnpm is available
133if ! command -v pnpm &> /dev/null; then
134 echo "Error: pnpm is not available after setup"
135 exit 1
136fi
137
138echo "pnpm version: $(pnpm --version)"
139
140# Point pnpm store at the Docker named volume so it persists across rebuilds
141pnpm config set store-dir /home/codespace/.local/share/pnpm/store --global
142echo "pnpm store-dir: $(pnpm store path)"
143
144echo ""
145echo "Installing system libraries required by TypeAgent..."
146# libsecret is required by keytar / native credential storage used by some
147# TypeAgent packages (libsecret-1.so.0 at runtime, libsecret-1-dev for builds).
148APT_PACKAGES=(
149 libsecret-1-0
150 libsecret-1-dev
151)
152# Skip if already baked into the image (via .devcontainer/Dockerfile)
153MISSING_PKGS=()
154for pkg in "${APT_PACKAGES[@]}"; do
155 if ! dpkg -s "$pkg" &>/dev/null; then
156 MISSING_PKGS+=("$pkg")
157 fi
158done
159if [[ ${#MISSING_PKGS[@]} -gt 0 ]]; then
160 if command -v apt-get &> /dev/null; then
161 if ! sudo DEBIAN_FRONTEND=noninteractive apt-get update -y; then
162 echo " warn: apt-get update failed"
163 fi
164 if ! sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends "${MISSING_PKGS[@]}"; then
165 echo " warn: failed to install: ${MISSING_PKGS[*]}"
166 fi
167 else
168 echo " warn: apt-get not available, skipping system library install"
169 fi
170else
171 echo " all packages already installed"
172fi
173
174echo ""
175echo "Configuring Git identity..."
176CURRENT_GIT_NAME=$(git config --global --get user.name 2>/dev/null || true)
177CURRENT_GIT_EMAIL=$(git config --global --get user.email 2>/dev/null || true)
178DESIRED_GIT_NAME="${LOCAL_GIT_USER_NAME:-}"
179DESIRED_GIT_EMAIL="${LOCAL_GIT_USER_EMAIL:-}"
180
181if [[ -n "$CURRENT_GIT_NAME" ]]; then
182 echo " git user.name already set"
183elif [[ -n "$DESIRED_GIT_NAME" ]]; then
184 git config --global user.name "$DESIRED_GIT_NAME"
185 echo " git user.name set"
186fi
187
188if [[ -n "$CURRENT_GIT_EMAIL" ]]; then
189 echo " git user.email already set"
190elif [[ -n "$DESIRED_GIT_EMAIL" ]]; then
191 git config --global user.email "$DESIRED_GIT_EMAIL"
192 echo " git user.email set"
193fi
194
195if [[ -z "$CURRENT_GIT_NAME" && -z "$DESIRED_GIT_NAME" ]] || \
196 [[ -z "$CURRENT_GIT_EMAIL" && -z "$DESIRED_GIT_EMAIL" ]]; then
197 echo ""
198 echo " Warning: no host git identity provided."
199 echo " Start the container via .devcontainer/scripts/start-devcontainer.sh"
200 echo " to inherit host ~/.gitconfig, or set it manually inside the container:"
201 echo " git config --global user.name \"Your Name\""
202 echo " git config --global user.email \"you@example.com\""
203fi
204
205# Install dependencies
206echo ""
207echo "Installing pnpm dependencies..."
208echo "This may take a few minutes on first run..."
209if ! pnpm install; then
210 echo ""
211 echo "Error: pnpm install failed." >&2
212 echo "This is often due to network issues or missing system dependencies." >&2
213 exit 1
214fi
215
216# - Security hardening: restrict sudo to a minimal allowlist
217# During post-create we needed unrestricted root access to install
218# packages and fix volume ownership. Now that setup is done, replace
219# the blanket NOPASSWD:ALL rule with the narrowest set of commands the
220# codespace user is likely to need at runtime.
221echo ""
222echo "Hardening sudo access..."
223SUDOERS_FILE="/etc/sudoers.d/codespace-restricted"
224sudo tee "$SUDOERS_FILE" > /dev/null << 'SUDOERS'
225# Restricted sudo for the codespace user (post-setup hardening).
226# Only allow package management, ownership fixes, and directory creation.
227codespace ALL=(root) NOPASSWD: /usr/bin/apt-get update*, \
228 /usr/bin/apt-get install*, \
229 /usr/bin/apt-get upgrade*, \
230 /usr/bin/apt-get autoremove*, \
231 /usr/bin/apt-get remove*, \
232 /usr/bin/dpkg -i *, \
233 /usr/bin/dpkg --configure *, \
234 /bin/chown *, \
235 /usr/bin/chown *, \
236 /bin/mkdir *, \
237 /usr/bin/mkdir *, \
238 /usr/sbin/service ssh *, \
239 /usr/sbin/service sshd *
240SUDOERS
241sudo chmod 0440 "$SUDOERS_FILE"
242# Remove the blanket rule that grants unrestricted root. The common-utils
243# devcontainer feature writes it to /etc/sudoers.d/codespace (filename
244# matches the username).
245if [[ -f /etc/sudoers.d/codespace ]]; then
246 sudo rm /etc/sudoers.d/codespace
247 echo " Removed blanket NOPASSWD:ALL rule"
248fi
249echo " Sudo restricted to: apt-get, dpkg, chown, mkdir, service ssh"
250
251echo ""
252echo "╔══════════════════════════════════════════════════════════════╗"
253echo "║ Setup Complete! ║"
254echo "╚══════════════════════════════════════════════════════════════╝"
255echo ""
256echo "Next steps:"
257echo " cd ts"
258echo " pnpm run build"
259echo ""
260
261case $ENV in
262 wsl2-gui)
263 echo "GUI Support: WSLg detected - 'pnpm run shell' will work!"
264 ;;
265 codespaces)
266 echo "GUI Support: Use VNC at http://localhost:6080"
267 ;;
268 *)
269 echo "GUI Support: For Electron, use hybrid approach:"
270 echo " Container: pnpm run server"
271 echo " Host: pnpm run shell"
272 ;;
273esac
274
275echo ""
276