UNPKG

@puls-atlas/cli

Version:

The Puls Atlas CLI tool for managing Atlas projects

182 lines 6.27 kB
import { createHash } from 'node:crypto'; import { normalizeOptionalString } from '../../utils/value.js'; const SYNC_RELEASE_ID_PREFIX = 'sync-release-'; export const normalizeSyncReleaseConfig = releaseConfig => ({ active: normalizeOptionalString(releaseConfig?.active), candidate: normalizeOptionalString(releaseConfig?.candidate), strategy: releaseConfig?.strategy === 'direct' ? 'direct' : 'managed' }); export const resolveSyncReleaseTarget = releaseConfig => { const normalizedConfig = normalizeSyncReleaseConfig(releaseConfig); if (normalizedConfig.strategy !== 'managed') { return { ...normalizedConfig, releaseId: null, target: 'direct' }; } if (normalizedConfig.candidate) { return { ...normalizedConfig, releaseId: normalizedConfig.candidate, target: 'candidate' }; } if (normalizedConfig.active) { return { ...normalizedConfig, releaseId: normalizedConfig.active, target: 'active' }; } return { ...normalizedConfig, releaseId: null, target: 'unassigned' }; }; export const createSyncReleaseId = (now = new Date()) => { const timestamp = now.toISOString().replace(/[-:.]/g, '').toLowerCase(); return `${SYNC_RELEASE_ID_PREFIX}${timestamp}`; }; export const createSyncConfigFingerprint = value => createHash('sha1').update(JSON.stringify(value)).digest('hex'); export const promoteSyncReleaseState = cachedState => { const normalizedRelease = normalizeSyncReleaseConfig(cachedState?.release); const candidateFingerprint = normalizeOptionalString(cachedState?.rollout?.candidateConfigFingerprint) ?? normalizeOptionalString(cachedState?.rollout?.configFingerprint); if (normalizedRelease.strategy !== 'managed') { throw new Error('Atlas sync release promotion requires the managed release strategy.'); } if (!normalizedRelease.candidate) { throw new Error('Atlas sync release promotion requires an existing candidate release in the local sync rollout cache.'); } if (!candidateFingerprint) { throw new Error('Atlas sync release promotion requires a candidate config fingerprint in the local sync rollout cache.'); } const release = { active: normalizedRelease.candidate, candidate: null, strategy: normalizedRelease.strategy }; return { release, releaseTarget: resolveSyncReleaseTarget(release), rollout: { activeConfigFingerprint: candidateFingerprint, backfillTarget: null, candidateConfigFingerprint: null, configFingerprint: candidateFingerprint, mode: 'active', readTarget: 'active', requiresCutover: false, writeTarget: 'active' } }; }; export const resolveSyncRolloutPlan = (currentConfigFingerprint, cachedState = null, options = {}) => { const now = options.now ?? new Date(); const previousRelease = normalizeSyncReleaseConfig(cachedState?.release); const previousRollout = cachedState?.rollout ?? {}; const previousActiveFingerprint = normalizeOptionalString(previousRollout.activeConfigFingerprint) ?? null; const previousCandidateFingerprint = normalizeOptionalString(previousRollout.candidateConfigFingerprint) ?? null; if (previousRelease.strategy === 'direct') { const release = normalizeSyncReleaseConfig({ strategy: 'direct' }); return { release, releaseTarget: resolveSyncReleaseTarget(release), rollout: { activeConfigFingerprint: null, backfillTarget: null, candidateConfigFingerprint: null, configFingerprint: currentConfigFingerprint, mode: 'direct', readTarget: 'direct', requiresCutover: false, writeTarget: 'direct' } }; } if (!previousRelease.active) { const release = normalizeSyncReleaseConfig({ active: previousRelease.candidate ?? createSyncReleaseId(now), candidate: null, strategy: 'managed' }); return { release, releaseTarget: resolveSyncReleaseTarget(release), rollout: { activeConfigFingerprint: currentConfigFingerprint, backfillTarget: null, candidateConfigFingerprint: null, configFingerprint: currentConfigFingerprint, mode: 'bootstrap', readTarget: 'active', requiresCutover: false, writeTarget: 'active' } }; } if (previousActiveFingerprint === currentConfigFingerprint) { const release = normalizeSyncReleaseConfig({ active: previousRelease.active, candidate: null, strategy: 'managed' }); return { release, releaseTarget: resolveSyncReleaseTarget(release), rollout: { activeConfigFingerprint: currentConfigFingerprint, backfillTarget: null, candidateConfigFingerprint: null, configFingerprint: currentConfigFingerprint, mode: 'active', readTarget: 'active', requiresCutover: false, writeTarget: 'active' } }; } if (previousRelease.candidate && previousCandidateFingerprint === currentConfigFingerprint) { const release = normalizeSyncReleaseConfig({ active: previousRelease.active, candidate: previousRelease.candidate, strategy: 'managed' }); return { release, releaseTarget: resolveSyncReleaseTarget(release), rollout: { activeConfigFingerprint: previousActiveFingerprint, backfillTarget: 'candidate', candidateConfigFingerprint: currentConfigFingerprint, configFingerprint: currentConfigFingerprint, mode: 'candidate-backfill', readTarget: 'active', requiresCutover: true, writeTarget: 'active' } }; } const release = normalizeSyncReleaseConfig({ active: previousRelease.active, candidate: createSyncReleaseId(now), strategy: 'managed' }); return { release, releaseTarget: resolveSyncReleaseTarget(release), rollout: { activeConfigFingerprint: previousActiveFingerprint, backfillTarget: 'candidate', candidateConfigFingerprint: currentConfigFingerprint, configFingerprint: currentConfigFingerprint, mode: 'candidate-backfill', readTarget: 'active', requiresCutover: true, writeTarget: 'active' } }; };