microsoft/TypeAgent

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
copilot/fix-github-actions-job-shell-and-cli

Branches

Tags

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

Clone

HTTPS

Download ZIP

.devcontainer/scripts/post-create.sh

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