Skip to content

Instantly share code, notes, and snippets.

@JKamsker
Last active September 24, 2025 13:56
Show Gist options
  • Select an option

  • Save JKamsker/66bda2c1196276339a479ea1857cf606 to your computer and use it in GitHub Desktop.

Select an option

Save JKamsker/66bda2c1196276339a479ea1857cf606 to your computer and use it in GitHub Desktop.
codex-web-installscripts
#!/bin/bash
# Parameterizable .NET setup script with parallel execution, smart caching, and tool integration.
# install with eg: curl -sSL ... | bash -s -- -v 7.0 -t "dotnet-ef,dotnet-aspnet-codegenerator,dotnet-try"
set -e
# --- Default Configuration ---
DOTNET_VERSION="8.0" # Default .NET version
APP_DIR="/app" # Default application directory
# Default global tools. If a tool name is passed without a version,
# it will be automatically pinned to the major .NET version being installed (e.g., "dotnet-ef" -> "dotnet-ef:8.*").
DEFAULT_TOOLS=(
"dotnet-ef"
"dotnet-aspnet-codegenerator"
"dotnet-dev-certs"
)
GLOBAL_TOOLS=() # This will be populated based on user input or defaults
# --- Argument Parsing ---
while [[ "$#" -gt 0 ]]; do
case $1 in
-v|--version) DOTNET_VERSION="$2"; shift ;;
-a|--app-dir) APP_DIR="$2"; shift ;;
-t|--tools) IFS=',' read -r -a GLOBAL_TOOLS <<< "$2"; shift ;;
-h|--help)
echo "Usage: $0 [-v version] [-a app_dir] [-t tool1,tool2:version,...]"
echo " -v, --version .NET version to install (e.g., 8.0, 7.0, 6.0). Default: 8.0"
echo " -a, --app-dir Application directory for 'dotnet restore'. Default: /app"
echo " -t, --tools Comma-separated list of global tools to install."
echo " (e.g., \"dotnet-ef,some-tool:1.2.3\")"
exit 0
;;
*) echo "Unknown parameter passed: $1"; exit 1 ;;
esac
shift
done
# --- Final Configuration Setup ---
DOTNET_INSTALL_DIR="$HOME/.dotnet"
DOTNET_CHANNEL="$DOTNET_VERSION" # The channel typically matches the major.minor version
DOTNET_MAJOR_VERSION=$(echo "$DOTNET_VERSION" | cut -d. -f1)
# If no custom tools were provided, use the defaults
if [ ${#GLOBAL_TOOLS[@]} -eq 0 ]; then
GLOBAL_TOOLS=("${DEFAULT_TOOLS[@]}")
fi
# Auto-pin tool versions if not specified
for i in "${!GLOBAL_TOOLS[@]}"; do
tool_spec="${GLOBAL_TOOLS[i]}"
if [[ "$tool_spec" != *":"* ]]; then
# If no version is specified, pin it to the major .NET version
GLOBAL_TOOLS[i]="${tool_spec}:${DOTNET_MAJOR_VERSION}.*"
fi
done
# --- Helper Functions ---
command_exists() { command -v "$1" >/dev/null 2>&1; }
add_to_path_if_missing() {
local path_entry="$1"
local bashrc_path="$HOME/.bashrc"
if ! grep -qF "$path_entry" "$bashrc_path" 2>/dev/null; then
echo "$path_entry" >> "$bashrc_path"
fi
}
setup_current_path() {
export DOTNET_ROOT="$DOTNET_INSTALL_DIR"
export PATH="$PATH:$DOTNET_ROOT:$HOME/.dotnet/tools"
}
setup_project_deps() {
if [ -d "$APP_DIR" ]; then
(
cd "$APP_DIR"
if find . -maxdepth 2 -name "*.sln" | head -1 | read -r sln_file; then
echo "Restoring solution packages for $sln_file..."
dotnet restore "$sln_file" --verbosity quiet
elif find . -maxdepth 2 -name "*.csproj" -o -name "*.fsproj" -o -name "*.vbproj" | head -1 | read -r proj_file; then
echo "Restoring project packages for $proj_file..."
dotnet restore "$proj_file" --verbosity quiet
else
echo "ℹ️ No .NET project or solution files found in $APP_DIR"
fi
) &
echo $!
else
echo "0"
fi
}
install_global_tools() {
echo "Installing/updating .NET global tools..."
local pids=()
for tool_spec in "${GLOBAL_TOOLS[@]}"; do
local tool_name="${tool_spec%%:*}"
local tool_version="${tool_spec##*:}"
local version_arg=""
if [[ "$tool_name" != "$tool_version" ]]; then
version_arg="--version $tool_version"
fi
echo "Installing/updating $tool_spec..."
# Use 'update' which installs if not present, making it idempotent
dotnet tool update --global "$tool_name" $version_arg --verbosity quiet &
pids+=($!)
done
for pid in "${pids[@]}"; do wait "$pid" 2>/dev/null || true; done
echo "✅ Global tools setup complete"
}
# --- Main Execution Logic ---
echo "🚀 Starting .NET setup for version $DOTNET_VERSION..."
echo " - App Directory: $APP_DIR"
echo " - Global Tools: ${GLOBAL_TOOLS[*]}"
if command_exists dotnet && dotnet --version >/dev/null 2>&1; then
INSTALLED_VERSION=$(dotnet --version)
if [[ "$INSTALLED_VERSION" == "$DOTNET_VERSION"* ]]; then
echo "✅ .NET version $DOTNET_VERSION is already installed."
setup_current_path
install_global_tools &
TOOLS_PID=$!
PROJECT_PID=$(setup_project_deps)
wait $TOOLS_PID 2>/dev/null || true
if [ "$PROJECT_PID" -ne 0 ]; then wait $PROJECT_PID 2>/dev/null || true; fi
echo "✅ Script execution complete (.NET was already set up)!"
exit 0
else
echo "Found different .NET version ($INSTALLED_VERSION). Installing $DOTNET_VERSION."
fi
fi
# --- Parallel Setup: System Dependencies & .NET Installation Script ---
echo "Setting up system dependencies and downloading .NET installer..."
{
export DEBIAN_FRONTEND=noninteractive
sudo -E apt-get update -qq
sudo -E apt-get install -y -qq --no-install-recommends \
wget ca-certificates git curl libc6 libgcc1 libgssapi-krb5-2 libicu-dev libssl-dev libstdc++6 zlib1g || {
echo "Warning: Some system packages may have failed to install."
}
echo "✅ System dependencies installed"
} & DEPS_PID=$!
TEMP_DIR=$(mktemp -d)
{
cd "$TEMP_DIR" && wget -q https://dot.net/v1/dotnet-install.sh -O dotnet-install.sh && chmod +x dotnet-install.sh
echo "✅ .NET installer downloaded"
} & DOWNLOAD_PID=$!
wait $DEPS_PID && wait $DOWNLOAD_PID
# --- Install .NET ---
echo "Installing .NET $DOTNET_VERSION..."
ARCH=$(dpkg --print-architecture)
case $ARCH in
amd64) DOTNET_ARCH="x64" ;;
arm64) DOTNET_ARCH="arm64" ;;
*) echo "Unsupported architecture: $ARCH"; exit 1 ;;
esac
"$TEMP_DIR/dotnet-install.sh" --channel "$DOTNET_CHANNEL" --install-dir "$DOTNET_INSTALL_DIR" --architecture "$DOTNET_ARCH" --no-path
rm -rf "$TEMP_DIR"
# --- Setup PATH and Environment ---
echo "Configuring PATH and environment variables..."
add_to_path_if_missing "export DOTNET_ROOT=\"$DOTNET_INSTALL_DIR\""
add_to_path_if_missing 'export PATH="$PATH:$DOTNET_ROOT:$HOME/.dotnet/tools"'
add_to_path_if_missing "export DOTNET_CLI_TELEMETRY_OPTOUT=1"
add_to_path_if_missing "export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1"
add_to_path_if_missing "export DOTNET_NOLOGO=1"
setup_current_path # For the current session
export DOTNET_CLI_TELEMETRY_OPTOUT=1 DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 DOTNET_NOLOGO=1
if ! command_exists dotnet; then echo "❌ Error: 'dotnet' command not found after installation."; exit 1; fi
echo "✅ .NET $(dotnet --version) installed successfully"
# --- Post-Install Configuration ---
echo "Running post-install tasks..."
install_global_tools &
TOOLS_PID=$!
PROJECT_PID=$(setup_project_deps)
wait $TOOLS_PID 2>/dev/null || true
if [ "$PROJECT_PID" -ne 0 ]; then wait $PROJECT_PID 2>/dev/null || true; fi
echo "✅ .NET $DOTNET_VERSION setup complete!"
echo "Installed components:"
echo " - .NET SDK: $(dotnet --version)"
echo " - Global Tools: ${GLOBAL_TOOLS[*]}"
echo "💡 Run 'source ~/.bashrc' or restart your shell to apply PATH changes permanently."
#!/bin/bash
# Optimized .NET 6 setup script with parallel execution, smart caching, EF integration, and Git CLI
set -e
# Configuration
DOTNET_INSTALL_DIR="$HOME/.dotnet"
APP_DIR="/app" # Default application directory in Jules VM
DOTNET_VERSION="6.0"
DOTNET_CHANNEL="6.0"
# Global tools to install, with version pinning for compatibility with .NET 6
# Format: "tool-name:version-string" or just "tool-name" for latest
GLOBAL_TOOLS=(
"dotnet-ef:6.*"
"dotnet-aspnet-codegenerator:6.*"
"dotnet-dev-certs"
)
# Function to check if command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Function to add PATH to bashrc only if not present
add_to_path_if_missing() {
local path_entry="$1"
local bashrc_path="$HOME/.bashrc"
if ! grep -qF "$path_entry" "$bashrc_path" 2>/dev/null; then
echo "$path_entry" >> "$bashrc_path"
echo "Added to PATH: $path_entry"
fi
}
# Function to setup PATH for current session
setup_current_path() {
export PATH="$PATH:$DOTNET_INSTALL_DIR:$HOME/.dotnet/tools"
export DOTNET_ROOT="$DOTNET_INSTALL_DIR"
}
echo "Starting optimized .NET 6 and Git setup..."
# Function to setup project dependencies
setup_project_deps() {
if [ -d "$APP_DIR" ]; then
(
cd "$APP_DIR"
# Look for solution files first, then project files
if find . -maxdepth 2 -name "*.sln" | head -1 | read -r sln_file; then
echo "Found solution file: $sln_file"
echo "Restoring solution packages..."
dotnet restore "$sln_file" --verbosity quiet
echo "✅ Solution packages restored"
elif find . -maxdepth 2 -name "*.csproj" -o -name "*.fsproj" -o -name "*.vbproj" | head -1 | read -r proj_file; then
echo "Found project file: $proj_file"
echo "Restoring project packages..."
dotnet restore "$proj_file" --verbosity quiet
echo "✅ Project packages restored"
else
echo "ℹ️ No .NET project or solution files found"
fi
) &
echo $! # Return PID of background process
else
echo "0" # Indicate no project directory
fi
}
# Function to install global tools (UPDATED LOGIC)
install_global_tools() {
echo "Installing .NET global tools..."
local pids=()
for tool_spec in "${GLOBAL_TOOLS[@]}"; do
# Parse tool name and version from "name:version" format
local tool_name="${tool_spec%%:*}"
local tool_version="${tool_spec##*:}"
local version_arg=""
if [[ "$tool_name" != "$tool_version" ]]; then
version_arg="--version $tool_version"
fi
if ! dotnet tool list -g | grep -q "$tool_name"; then
echo "Installing $tool_spec..."
# Note: The $version_arg is intentionally not quoted to allow it to be empty
dotnet tool install --global "$tool_name" $version_arg --verbosity quiet &
pids+=($!)
else
echo "✅ $tool_name already installed"
fi
done
# Wait for all tool installations to complete
for pid in "${pids[@]}"; do
wait "$pid" 2>/dev/null || true
done
echo "✅ Global tools installation complete"
}
# Check if .NET is already installed and working
if command_exists dotnet && dotnet --version >/dev/null 2>&1; then
INSTALLED_VERSION=$(dotnet --version | cut -d. -f1-2)
if [ "$INSTALLED_VERSION" = "$DOTNET_VERSION" ]; then
echo "✅ .NET $DOTNET_VERSION already installed and working"
setup_current_path
install_global_tools &
TOOLS_PID=$!
if [ -d "$APP_DIR" ]; then
echo "Setting up project dependencies..."
PROJECT_PID=$(setup_project_deps)
if [ "$PROJECT_PID" -ne 0 ]; then
wait $PROJECT_PID 2>/dev/null || true
fi
fi
wait $TOOLS_PID 2>/dev/null || true
echo "✅ All dependencies setup complete"
echo "✅ Script execution complete (.NET was already set up)!"
exit 0
else
echo "Different .NET version found ($INSTALLED_VERSION), will install .NET $DOTNET_VERSION"
fi
fi
# --- Parallel Setup: System Dependencies & .NET Installation ---
echo "Setting up system dependencies and .NET in parallel..."
{
echo "Installing system dependencies..."
export DEBIAN_FRONTEND=noninteractive
sudo -E apt-get update -qq
sudo -E apt-get install -y -qq --no-install-recommends \
-o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" \
wget ca-certificates apt-transport-https software-properties-common \
libc6 libgcc1 libgssapi-krb5-2 libicu-dev libssl-dev libstdc++6 zlib1g \
curl git || {
echo "Warning: Some packages may have failed to install, continuing..."
}
echo "✅ System dependencies installation complete"
} &
DEPS_PID=$!
ARCH=$(dpkg --print-architecture)
case $ARCH in
amd64) DOTNET_ARCH="x64" ;;
arm64) DOTNET_ARCH="arm64" ;;
armhf) DOTNET_ARCH="arm" ;;
*) echo "Unsupported architecture: $ARCH"; exit 1 ;;
esac
TEMP_DIR=$(mktemp -d)
{
echo "Downloading .NET $DOTNET_VERSION..."
cd "$TEMP_DIR"
wget -q https://dot.net/v1/dotnet-install.sh -O dotnet-install.sh
chmod +x dotnet-install.sh
echo "✅ .NET download preparation complete"
} &
DOWNLOAD_PID=$!
wait $DEPS_PID
wait $DOWNLOAD_PID
# --- Install .NET ---
echo "Installing .NET $DOTNET_VERSION..."
cd "$TEMP_DIR"
./dotnet-install.sh --channel $DOTNET_CHANNEL --install-dir "$DOTNET_INSTALL_DIR" --architecture "$DOTNET_ARCH" --verbose
cd /
rm -rf "$TEMP_DIR"
# --- Setup PATH ---
echo "Configuring PATH..."
add_to_path_if_missing "export PATH=\"\$PATH:$DOTNET_INSTALL_DIR\""
add_to_path_if_missing "export PATH=\"\$PATH:\$HOME/.dotnet/tools\""
add_to_path_if_missing "export DOTNET_ROOT=\"$DOTNET_INSTALL_DIR\""
setup_current_path
if ! command_exists dotnet; then
echo "❌ Error: .NET command not found after installation"
exit 1
fi
echo "✅ .NET $(dotnet --version) installed successfully"
# --- .NET Configuration ---
echo "Configuring .NET..."
{
export DOTNET_CLI_TELEMETRY_OPTOUT=1
export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
export DOTNET_NOLOGO=1
add_to_path_if_missing "export DOTNET_CLI_TELEMETRY_OPTOUT=1"
add_to_path_if_missing "export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1"
add_to_path_if_missing "export DOTNET_NOLOGO=1"
} &
{
echo "Warming up .NET CLI..."
dotnet --info >/dev/null 2>&1
echo "✅ .NET CLI warmed up"
} &
install_global_tools &
TOOLS_PID=$!
if [ -d "$APP_DIR" ]; then
echo "App directory found, will restore packages after .NET setup..."
NEED_DEPS=true
else
echo "No app directory found at $APP_DIR"
NEED_DEPS=false
fi
wait
if [ "$NEED_DEPS" = true ]; then
echo "Installing project dependencies..."
PROJECT_PID=$(setup_project_deps)
if [ "$PROJECT_PID" -ne 0 ]; then
wait $PROJECT_PID 2>/dev/null || true
fi
if [ -d "$APP_DIR" ]; then
(
cd "$APP_DIR"
if find . -name "*.csproj" -exec grep -l "Microsoft.EntityFrameworkCore" {} \; | head -1 >/dev/null 2>&1; then
echo "Entity Framework detected, verifying EF tools..."
if ! command_exists dotnet-ef; then
echo "Installing Entity Framework tools for .NET 6..."
# CORRECTED FALLBACK INSTALL
dotnet tool install --global dotnet-ef --version "6.*" --verbosity quiet
else
echo "✅ Entity Framework tools ready"
fi
fi
)
fi
fi
wait $TOOLS_PID 2>/dev/null || true
echo "✅ Optimized .NET 6 and Git setup complete!"
echo ""
echo "Installed components:"
echo " - .NET $(dotnet --version)"
echo " - Git CLI"
echo " - Entity Framework Core tools"
echo " - ASP.NET Core code generator"
echo " - Development certificates tool"
echo ""
echo "Run 'source ~/.bashrc' or restart your shell to use the tools in new sessions."
echo ""
echo "Quick start commands:"
echo " dotnet --version # Check .NET version"
echo " git --version # Check Git version"
echo " dotnet new --list # List project templates"
echo " dotnet ef --version # Check EF tools version"
#!/usr/bin/env bash
set -euo pipefail
SA_PASSWORD="${SA_PASSWORD:-ChangeMe_SA_Th!sIsStr0ng}"
APP_LOGIN="${APP_LOGIN:-myapp_login}"
APP_PASSWORD="${APP_PASSWORD:-ChangeMe_App_Th!sIsStr0ng}"
APP_DATABASE="${APP_DATABASE:-MyAppDb}"
MSSQL_PID="${MSSQL_PID:-Developer}"
ARCH="$(dpkg --print-architecture)"
KEYRING="/usr/share/keyrings/microsoft-prod.gpg"
apt-get update -y
apt-get install -y --no-install-recommends \
curl ca-certificates gnupg locales procps iproute2 \
libunwind8 libuuid1 libkrb5-3 libgssapi-krb5-2 lsof \
software-properties-common
install -d -m 0755 /usr/share/keyrings
if [[ ! -f "${KEYRING}" ]]; then
curl -fsSL https://packages.microsoft.com/keys/microsoft.asc \
| gpg --dearmor \
| tee "${KEYRING}" > /dev/null
fi
cat <<REPO >/etc/apt/sources.list.d/mssql-server-preview.list
deb [arch=${ARCH} signed-by=${KEYRING}] https://packages.microsoft.com/ubuntu/22.04/mssql-server-preview jammy main
REPO
cat <<REPO >/etc/apt/sources.list.d/jammy-ldap.list
deb [arch=${ARCH}] http://archive.ubuntu.com/ubuntu jammy main
deb [arch=${ARCH}] http://security.ubuntu.com/ubuntu jammy-security main
REPO
cat <<REPO >/etc/apt/sources.list.d/msprod.list
deb [arch=${ARCH} signed-by=${KEYRING}] https://packages.microsoft.com/ubuntu/22.04/prod jammy main
REPO
cat <<'PREF' >/etc/apt/preferences.d/mssql-libldap.pref
Package: libldap-2.5-0 libldap-common
Pin: release n=jammy-security
Pin-Priority: 1001
Package: libldap-2.5-0 libldap-common
Pin: release n=jammy
Pin-Priority: 1000
Package: libldap-2.5-0 libldap-common
Pin: release n=noble
Pin-Priority: -1
PREF
apt-get update -y
apt-get install -y --allow-downgrades libldap-2.5-0/jammy-security libldap-common/jammy-security
ACCEPT_EULA=Y apt-get install -y mssql-server msodbcsql18 mssql-tools18 unixodbc unixodbc-dev
if [[ ! -f /var/opt/mssql/data/master.mdf ]]; then
ACCEPT_EULA=Y MSSQL_SA_PASSWORD="${SA_PASSWORD}" MSSQL_PID="${MSSQL_PID}" \
/opt/mssql/bin/mssql-conf -n setup || true
else
echo "SQL Server data directory detected; resetting SA password to the configured value."
chown -R mssql:mssql /var/opt/mssql
ACCEPT_EULA=Y MSSQL_SA_PASSWORD="${SA_PASSWORD}" \
/opt/mssql/bin/mssql-conf -n set-sa-password || true
fi
cat <<MSG
✅ SQL Server packages installed.
To start the engine in this container run:
ACCEPT_EULA=Y /opt/mssql/bin/sqlservr
Once the engine is running you can connect with:
/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P '${SA_PASSWORD}' -C
MSG
if [[ -n "${APP_DATABASE}" ]] && [[ -n "${APP_LOGIN}" ]] && [[ -n "${APP_PASSWORD}" ]]; then
echo "Preparing optional application database bootstrap scripts in /var/opt/mssql."
cat <<SQL >/var/opt/mssql/create-app-database.sql
:setvar AppDb ${APP_DATABASE}
:setvar AppLogin ${APP_LOGIN}
:setvar AppPassword ${APP_PASSWORD}
IF DB_ID(N'\$(AppDb)') IS NULL
BEGIN
DECLARE @sql nvarchar(max) = N'CREATE DATABASE ' + QUOTENAME(N'\$(AppDb)');
EXEC (@sql);
END
GO
IF NOT EXISTS (SELECT 1 FROM sys.server_principals WHERE name = N'\$(AppLogin)')
BEGIN
DECLARE @sql nvarchar(max) = N'CREATE LOGIN ' + QUOTENAME(N'\$(AppLogin)')
+ N' WITH PASSWORD = ''' + REPLACE(N'\$(AppPassword)', '''', '''''') + N''', CHECK_POLICY = ON, CHECK_EXPIRATION = OFF';
EXEC (@sql);
END
GO
USE \$(AppDb);
GO
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = N'\$(AppLogin)')
BEGIN
DECLARE @sql nvarchar(max) = N'CREATE USER ' + QUOTENAME(N'\$(AppLogin)') + N' FOR LOGIN ' + QUOTENAME(N'\$(AppLogin)');
EXEC (@sql);
END
GO
IF NOT EXISTS (
SELECT 1
FROM sys.database_role_members drm
JOIN sys.database_principals rp ON rp.principal_id = drm.role_principal_id AND rp.name = N'db_owner'
JOIN sys.database_principals mp ON mp.principal_id = drm.member_principal_id AND mp.name = N'\$(AppLogin)'
)
BEGIN
DECLARE @sql nvarchar(max) = N'ALTER ROLE db_owner ADD MEMBER ' + QUOTENAME(N'\$(AppLogin)');
EXEC (@sql);
END
GO
SQL
cat <<INSTR
To apply the optional database bootstrap once SQL Server is running:
/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P '${SA_PASSWORD}' -C -i /var/opt/mssql/create-app-database.sql
INSTR
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment