UNPKG

iop

Version:
187 lines 6.91 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.createRuntimeHash = createRuntimeHash; exports.getLocalImageHash = getLocalImageHash; exports.isBuiltService = isBuiltService; exports.createServiceFingerprint = createServiceFingerprint; exports.shouldRedeploy = shouldRedeploy; const crypto = __importStar(require("crypto")); const child_process_1 = require("child_process"); const util_1 = require("util"); const execAsync = (0, util_1.promisify)(child_process_1.exec); /** * Creates a runtime hash combining configuration and secrets * This determines if containers need to be recreated (but not if image needs transfer) */ function createRuntimeHash(serviceEntry, secrets) { const runtimeConfig = { // Configuration that affects container runtime ports: serviceEntry.ports?.sort() || [], volumes: serviceEntry.volumes?.sort() || [], command: serviceEntry.command, replicas: serviceEntry.replicas || 1, restart: serviceEntry.restart, proxy: serviceEntry.proxy ? { hosts: serviceEntry.proxy.hosts?.sort() || [], app_port: serviceEntry.proxy.app_port, ssl: serviceEntry.proxy.ssl, ssl_redirect: serviceEntry.proxy.ssl_redirect, forward_headers: serviceEntry.proxy.forward_headers, response_timeout: serviceEntry.proxy.response_timeout, } : undefined, health_check: serviceEntry.health_check, // Environment variables (both plain and secret values) environment: { plain: serviceEntry.environment?.plain?.sort() || [], secretValues: (serviceEntry.environment?.secret || []) .sort() .reduce((acc, key) => { if (secrets[key] !== undefined) { acc[key] = secrets[key]; } return acc; }, {}), }, }; return crypto .createHash('sha256') .update(JSON.stringify(runtimeConfig)) .digest('hex') .substring(0, 12); } /** * Gets the Docker image hash for a locally built image */ async function getLocalImageHash(imageName) { try { const command = `docker inspect --format='{{.Id}}' ${imageName}:latest`; const result = await execAsync(command); const imageId = result.stdout.trim(); return imageId || null; } catch (error) { return null; // Image doesn't exist locally } } /** * Determines if a service is built locally or uses external image */ function isBuiltService(serviceEntry) { return !!serviceEntry.build; } /** * Creates a service fingerprint based on service type */ async function createServiceFingerprint(serviceEntry, secrets, projectName) { const runtimeHash = createRuntimeHash(serviceEntry, secrets); if (isBuiltService(serviceEntry)) { // Built service - get local image hash directly from Docker // Use same naming convention as getServiceImageName() const imageName = serviceEntry.name; const localImageHash = await getLocalImageHash(imageName); return { type: 'built', runtimeHash, localImageHash: localImageHash || undefined, }; } else { // External service - just track image reference and runtime config return { type: 'external', runtimeHash, imageReference: serviceEntry.image, }; } } /** * Determines if a service should be redeployed based on fingerprint comparison */ function shouldRedeploy(current, desired) { // No current fingerprint means first deployment if (!current) { return { shouldRedeploy: true, reason: 'first deployment', priority: 'normal', changeType: 'first-deployment', needsImageTransfer: true }; } // Check for image changes first (highest priority for transfer decisions) if (desired.type === 'built') { // Compare local desired image with current server image if (desired.localImageHash && current.serverImageHash !== desired.localImageHash) { return { shouldRedeploy: true, reason: 'image updated', priority: 'normal', changeType: 'image', needsImageTransfer: true }; } } // For external services, check image reference if (desired.type === 'external') { if (current.imageReference !== desired.imageReference) { return { shouldRedeploy: true, reason: 'image version updated', priority: 'normal', changeType: 'image', needsImageTransfer: false // External images are pulled, not transferred }; } } // Runtime changes (config + secrets) - no image transfer needed if (current.runtimeHash !== desired.runtimeHash) { return { shouldRedeploy: true, reason: 'configuration or secrets changed', priority: 'critical', changeType: 'runtime', needsImageTransfer: false }; } return { shouldRedeploy: false, reason: 'up-to-date, skipped', priority: 'optional', changeType: 'image', // doesn't matter since shouldRedeploy is false needsImageTransfer: false }; } //# sourceMappingURL=service-fingerprint.js.map