@puls-atlas/cli
Version:
The Puls Atlas CLI tool for managing Atlas projects
182 lines • 6.27 kB
JavaScript
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'
}
};
};