47 Local Installation and Team Distribution

47.1 VSIX Installation Methods

VSIX files can be installed directly without marketplace dependencies - comparable to local installation of JAR files from Maven repositories.

47.1.1 Command Line Installation

# Install single extension
code --install-extension path/to/extension.vsix

# Batch install multiple extensions
code --install-extension ext1.vsix --install-extension ext2.vsix

# With specific VSCode build
code-insiders --install-extension extension.vsix

# Force installation (overwrites existing version)
code --install-extension extension.vsix --force

47.1.2 UI-based Installation

Via VSCode user interface: 1. Command Palette (Ctrl+Shift+P) 2. “Extensions: Install from VSIX…” 3. Select VSIX file

47.1.3 Programmatic Installation

Extension code for automated installation:

// src/extensionInstaller.ts
export class LocalExtensionInstaller {
    public static async installFromVsix(vsixPath: string): Promise<boolean> {
        const uri = vscode.Uri.file(vsixPath);
        
        try {
            await vscode.commands.executeCommand(
                'workbench.extensions.installExtension', 
                uri
            );
            
            vscode.window.showInformationMessage(
                `Extension installed: ${path.basename(vsixPath)}`
            );
            return true;
        } catch (error) {
            vscode.window.showErrorMessage(
                `Installation failed: ${error.message}`
            );
            return false;
        }
    }

    public static async batchInstall(vsixPaths: string[]): Promise<InstallResult[]> {
        const results: InstallResult[] = [];
        
        for (const vsixPath of vsixPaths) {
            const success = await this.installFromVsix(vsixPath);
            results.push({
                path: vsixPath,
                success,
                timestamp: new Date()
            });
        }
        
        return results;
    }

    public static async verifyInstallation(extensionId: string): Promise<boolean> {
        const extension = vscode.extensions.getExtension(extensionId);
        return extension !== undefined;
    }
}

interface InstallResult {
    path: string;
    success: boolean;
    timestamp: Date;
}

47.2 Team Distribution via Shared Storage

47.2.1 Network-based Distribution

Central extension deployment via shared network drives:

// src/teamDistribution.ts
export interface TeamExtensionConfig {
    repositoryPath: string;
    manifestFile: string;
    autoUpdateEnabled: boolean;
    updateCheckInterval: number; // in milliseconds
}

export class TeamExtensionManager {
    constructor(private config: TeamExtensionConfig) {}

    public async getAvailableExtensions(): Promise<TeamExtension[]> {
        const manifestPath = path.join(
            this.config.repositoryPath, 
            this.config.manifestFile
        );
        
        try {
            const manifest = await fs.readFile(manifestPath, 'utf8');
            const data = JSON.parse(manifest);
            return data.extensions || [];
        } catch (error) {
            console.error('Failed to read team extension manifest:', error);
            return [];
        }
    }

    public async checkForUpdates(): Promise<ExtensionUpdate[]> {
        const availableExtensions = await this.getAvailableExtensions();
        const installedExtensions = vscode.extensions.all;
        
        const updates: ExtensionUpdate[] = [];
        
        for (const teamExt of availableExtensions) {
            const installed = installedExtensions.find(
                ext => ext.id === teamExt.id
            );
            
            if (installed && this.isNewerVersion(teamExt.version, installed.packageJSON.version)) {
                updates.push({
                    id: teamExt.id,
                    currentVersion: installed.packageJSON.version,
                    availableVersion: teamExt.version,
                    vsixPath: path.join(this.config.repositoryPath, teamExt.vsixFile)
                });
            }
        }
        
        return updates;
    }

    private isNewerVersion(available: string, current: string): boolean {
        // Simplified semantic version comparison
        const parseVersion = (v: string) => v.split('.').map(Number);
        const [aMajor, aMinor, aPatch] = parseVersion(available);
        const [cMajor, cMinor, cPatch] = parseVersion(current);
        
        if (aMajor !== cMajor) return aMajor > cMajor;
        if (aMinor !== cMinor) return aMinor > cMinor;
        return aPatch > cPatch;
    }
}

interface TeamExtension {
    id: string;
    displayName: string;
    version: string;
    vsixFile: string;
    required: boolean;
    description?: string;
}

interface ExtensionUpdate {
    id: string;
    currentVersion: string;
    availableVersion: string;
    vsixPath: string;
}

47.2.2 Team Extension Manifest

Structured configuration for team extensions:

{
    "metadata": {
        "version": "1.0",
        "lastUpdated": "2025-01-15T10:00:00Z",
        "maintainer": "dev-tools-team@company.com",
        "description": "Company VSCode Extensions Repository"
    },
    "extensions": [
        {
            "id": "company.typescript-tools",
            "displayName": "Company TypeScript Tools",
            "version": "1.2.3",
            "vsixFile": "company.typescript-tools-1.2.3.vsix",
            "required": true,
            "description": "Internal TypeScript development tools",
            "dependencies": [
                "ms-vscode.vscode-typescript-next"
            ],
            "configuration": {
                "company.typescript.strictMode": true,
                "company.typescript.autoImport": true
            }
        },
        {
            "id": "company.code-standards",
            "displayName": "Company Code Standards",
            "version": "2.1.0",
            "vsixFile": "company.code-standards-2.1.0.vsix",
            "required": false,
            "description": "Enforces company coding standards and best practices"
        }
    ],
    "installationScripts": {
        "windows": "scripts/install-windows.ps1",
        "linux": "scripts/install-linux.sh",
        "macos": "scripts/install-macos.sh"
    }
}

47.3 Automated Team Installation

47.3.1 PowerShell Script for Windows

# scripts/install-team-extensions.ps1
param(
    [Parameter(Mandatory=$true)]
    [string]$ExtensionPath,
    
    [string]$ManifestFile = "team-extensions.json",
    [switch]$RequiredOnly,
    [switch]$Force
)

$ErrorActionPreference = "Stop"

Write-Host "Team Extension Installer" -ForegroundColor Green
Write-Host "Repository: $ExtensionPath" -ForegroundColor Yellow

# Load and validate manifest
$ManifestPath = Join-Path $ExtensionPath $ManifestFile
if (-not (Test-Path $ManifestPath)) {
    Write-Error "Manifest file not found: $ManifestPath"
    exit 1
}

$Manifest = Get-Content $ManifestPath | ConvertFrom-Json
$Extensions = $Manifest.extensions

if ($RequiredOnly) {
    $Extensions = $Extensions | Where-Object { $_.required -eq $true }
}

Write-Host "Installing $($Extensions.Count) extensions..." -ForegroundColor Green

$SuccessCount = 0
$FailureCount = 0

foreach ($Extension in $Extensions) {
    $VsixPath = Join-Path $ExtensionPath $Extension.vsixFile
    
    if (-not (Test-Path $VsixPath)) {
        Write-Host "⚠️  VSIX not found: $($Extension.vsixFile)" -ForegroundColor Yellow
        $FailureCount++
        continue
    }
    
    Write-Host "📦 Installing $($Extension.displayName) v$($Extension.version)..." -ForegroundColor Cyan
    
    $InstallArgs = @("--install-extension", $VsixPath)
    if ($Force) {
        $InstallArgs += "--force"
    }
    
    try {
        & code @InstallArgs 2>$null
        
        if ($LASTEXITCODE -eq 0) {
            Write-Host "✅ $($Extension.displayName) installed successfully" -ForegroundColor Green
            $SuccessCount++
        } else {
            Write-Host "❌ Failed to install $($Extension.displayName)" -ForegroundColor Red
            $FailureCount++
        }
    } catch {
        Write-Host "❌ Exception installing $($Extension.displayName): $($_.Exception.Message)" -ForegroundColor Red
        $FailureCount++
    }
}

Write-Host "`nInstallation Summary:" -ForegroundColor Green
Write-Host "✅ Successful: $SuccessCount" -ForegroundColor Green
Write-Host "❌ Failed: $FailureCount" -ForegroundColor Red

if ($FailureCount -gt 0) {
    exit 1
}

47.3.2 Bash Script for Linux/macOS

#!/bin/bash
# scripts/install-team-extensions.sh

set -e

EXTENSION_PATH="${1?'Extension path required'}"
MANIFEST_FILE="${2:-team-extensions.json}"
REQUIRED_ONLY="${3:-false}"
FORCE="${4:-false}"

echo "🚀 Team Extension Installer"
echo "📁 Repository: $EXTENSION_PATH"

# Validation
if [ ! -d "$EXTENSION_PATH" ]; then
    echo "❌ Extension path does not exist: $EXTENSION_PATH"
    exit 1
fi

MANIFEST_PATH="$EXTENSION_PATH/$MANIFEST_FILE"
if [ ! -f "$MANIFEST_PATH" ]; then
    echo "❌ Manifest file not found: $MANIFEST_PATH"
    exit 1
fi

# jq required for JSON parsing
if ! command -v jq &> /dev/null; then
    echo "❌ jq is required but not installed"
    exit 1
fi

# Extract extensions from manifest
if [ "$REQUIRED_ONLY" = "true" ]; then
    EXTENSIONS=$(jq -r '.extensions[] | select(.required == true) | "\(.id)|\(.displayName)|\(.version)|\(.vsixFile)"' "$MANIFEST_PATH")
else
    EXTENSIONS=$(jq -r '.extensions[] | "\(.id)|\(.displayName)|\(.version)|\(.vsixFile)"' "$MANIFEST_PATH")
fi

SUCCESS_COUNT=0
FAILURE_COUNT=0

echo "$EXTENSIONS" | while IFS='|' read -r extension_id display_name version vsix_file; do
    [ -z "$extension_id" ] && continue
    
    vsix_path="$EXTENSION_PATH/$vsix_file"
    
    if [ ! -f "$vsix_path" ]; then
        echo "⚠️  VSIX not found: $vsix_file"
        ((FAILURE_COUNT++))
        continue
    fi
    
    echo "📦 Installing $display_name v$version..."
    
    install_args=("--install-extension" "$vsix_path")
    if [ "$FORCE" = "true" ]; then
        install_args+=("--force")
    fi
    
    if code "${install_args[@]}" 2>/dev/null; then
        echo "✅ $display_name installed successfully"
        ((SUCCESS_COUNT++))
    else
        echo "❌ Failed to install $display_name"
        ((FAILURE_COUNT++))
    fi
done

echo ""
echo "📊 Installation Summary:"
echo "✅ Successful: $SUCCESS_COUNT"
echo "❌ Failed: $FAILURE_COUNT"

[ $FAILURE_COUNT -eq 0 ] || exit 1

47.4 Automatic Update System

47.4.1 Update Checker Extension

// src/teamUpdateChecker.ts
export class TeamUpdateChecker {
    private readonly CHECK_INTERVAL = 24 * 60 * 60 * 1000; // 24 hours
    private updateTimer?: NodeJS.Timeout;
    private teamManager: TeamExtensionManager;

    constructor(
        private context: vscode.ExtensionContext,
        config: TeamExtensionConfig
    ) {
        this.teamManager = new TeamExtensionManager(config);
    }

    public startUpdateChecker(): void {
        // Immediate check on start
        this.checkForUpdates();
        
        // Periodic checks
        this.updateTimer = setInterval(() => {
            this.checkForUpdates();
        }, this.CHECK_INTERVAL);
        
        this.context.subscriptions.push(
            new vscode.Disposable(() => {
                if (this.updateTimer) {
                    clearInterval(this.updateTimer);
                }
            })
        );
    }

    private async checkForUpdates(): Promise<void> {
        try {
            const updates = await this.teamManager.checkForUpdates();
            
            if (updates.length === 0) {
                return;
            }

            const message = updates.length === 1 
                ? `1 extension update available`
                : `${updates.length} extension updates available`;

            const action = await vscode.window.showInformationMessage(
                message,
                'Update Now',
                'Update Later',
                'Show Details'
            );

            switch (action) {
                case 'Update Now':
                    await this.performUpdates(updates);
                    break;
                case 'Show Details':
                    await this.showUpdateDetails(updates);
                    break;
            }
        } catch (error) {
            console.error('Update check failed:', error);
            
            // Only notify user on critical errors
            if (error.code === 'ENOENT') {
                vscode.window.showWarningMessage(
                    'Team extension repository not accessible'
                );
            }
        }
    }

    private async performUpdates(updates: ExtensionUpdate[]): Promise<void> {
        await vscode.window.withProgress({
            location: vscode.ProgressLocation.Notification,
            title: 'Updating team extensions...',
            cancellable: false
        }, async (progress) => {
            let completed = 0;
            
            for (const update of updates) {
                progress.report({
                    message: `Updating ${update.id}...`,
                    increment: (100 / updates.length)
                });
                
                try {
                    const success = await LocalExtensionInstaller.installFromVsix(
                        update.vsixPath
                    );
                    
                    if (success) {
                        completed++;
                    }
                } catch (error) {
                    console.error(`Failed to update ${update.id}:`, error);
                }
            }
            
            const message = completed === updates.length
                ? `All ${completed} extensions updated successfully`
                : `${completed}/${updates.length} extensions updated`;
                
            vscode.window.showInformationMessage(message);
        });
    }

    private async showUpdateDetails(updates: ExtensionUpdate[]): Promise<void> {
        const items = updates.map(update => ({
            label: update.id,
            description: `${update.currentVersion}${update.availableVersion}`,
            update
        }));

        const selected = await vscode.window.showQuickPick(items, {
            placeHolder: 'Select extensions to update',
            canPickMany: true
        });

        if (selected && selected.length > 0) {
            const selectedUpdates = selected.map(item => item.update);
            await this.performUpdates(selectedUpdates);
        }
    }
}

47.5 Private Extension Repository

47.5.1 HTTP-based Distribution

Simple HTTP server for extension distribution:

// server/extensionServer.ts
import express from 'express';
import path from 'path';
import fs from 'fs/promises';

export class ExtensionServer {
    private app = express();
    
    constructor(private extensionPath: string, private port: number = 3000) {
        this.setupRoutes();
    }

    private setupRoutes(): void {
        // Serve static VSIX files
        this.app.use('/extensions', express.static(this.extensionPath));
        
        // Manifest API
        this.app.get('/api/manifest', async (req, res) => {
            try {
                const manifestPath = path.join(this.extensionPath, 'team-extensions.json');
                const manifest = await fs.readFile(manifestPath, 'utf8');
                res.json(JSON.parse(manifest));
            } catch (error) {
                res.status(404).json({ error: 'Manifest not found' });
            }
        });
        
        // Extension details
        this.app.get('/api/extensions/:id', async (req, res) => {
            try {
                const manifest = await this.getManifest();
                const extension = manifest.extensions.find(
                    ext => ext.id === req.params.id
                );
                
                if (!extension) {
                    return res.status(404).json({ error: 'Extension not found' });
                }
                
                res.json(extension);
            } catch (error) {
                res.status(500).json({ error: 'Server error' });
            }
        });
    }

    private async getManifest(): Promise<any> {
        const manifestPath = path.join(this.extensionPath, 'team-extensions.json');
        const content = await fs.readFile(manifestPath, 'utf8');
        return JSON.parse(content);
    }

    public start(): void {
        this.app.listen(this.port, () => {
            console.log(`Extension server running on port ${this.port}`);
        });
    }
}

47.5.2 Docker Deployment

# Dockerfile for extension repository
FROM node:18-alpine

WORKDIR /app

# Copy server code
COPY server/ ./server/
COPY package*.json ./

RUN npm ci --only=production

# Mount extension repository
VOLUME ["/extensions"]

EXPOSE 3000

CMD ["node", "server/extensionServer.js"]

Docker Compose for complete solution:

# docker-compose.yml
version: '3.8'

services:
  extension-repo:
    build: .
    ports:
      - "3000:3000"
    volumes:
      - ./extensions:/extensions:ro
      - ./config:/config:ro
    environment:
      - EXTENSION_PATH=/extensions
      - CONFIG_PATH=/config
    restart: unless-stopped

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - extension-repo
    restart: unless-stopped

47.6 Best Practices for Team Distribution

47.6.1 Version Control and Rollback

// src/versionManager.ts
export class ExtensionVersionManager {
    private readonly BACKUP_DIR = path.join(os.homedir(), '.vscode-extensions-backup');

    public async backupCurrentVersion(extensionId: string): Promise<string | null> {