VSIX files can be installed directly without marketplace dependencies - comparable to local installation of JAR files from Maven repositories.
# 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 --forceVia VSCode user interface: 1. Command Palette
(Ctrl+Shift+P) 2. “Extensions: Install from VSIX…” 3.
Select VSIX file
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;
}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;
}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"
}
}# 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
}#!/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// 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);
}
}
}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}`);
});
}
}# 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// 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> {