OverviewFilesCommitsIssuesDocsReleasesPeers

pear-git / lib / apply / deploy.js

91 lines · javascript ref:
/**
 * Deploy namespace apply handler for unified MetadataBase
 *
 * Extracted from lib/ci/deploy-apply.js
 * Processes single deploy operations within the unified apply batch.
 *
 * Key schema (unified):
 *   server:{serverKey}                → { serverKey, instanceUrl, hostname, status, registeredAt }
 *   deploy:{repoName}                 → { repoName, channel, commitHash, branch, deployedBy, deployedAt }
 *   deploy-log:{repoName}:{timestamp} → { repoName, serverKey, commitHash, branch, channel, timestamp }
 */

/**
 * Apply a single deploy-namespace operation
 *
 * @param {object} op - Operation with { ns: 'deploy', type, ... }
 * @param {object} batch - Hyperbee batch (caller flushes)
 * @param {object} base - Autobase host (for addWriter/removeWriter)
 */
export async function applyDeployOp (op, batch, base) {
  switch (op.type) {
    case 'register-server': {
      await batch.put(`server:${op.serverKey}`, {
        serverKey: op.serverKey,
        instanceUrl: op.instanceUrl || null,
        hostname: op.hostname || null,
        status: 'active',
        registeredAt: op.timestamp || 0
      })
      break
    }

    case 'deploy-complete': {
      // Update current deploy state (last-writer-wins via Autobase linearization)
      await batch.put(`deploy:${op.repoName}`, {
        repoName: op.repoName,
        channel: op.channel || op.repoName,
        commitHash: op.commitHash,
        branch: op.branch,
        deployedBy: op.serverKey || null,
        deployedAt: op.timestamp || 0
      })

      // Append to deploy log (immutable history)
      const ts = op.timestamp || 0
      await batch.put(`deploy-log:${op.repoName}:${ts}`, {
        repoName: op.repoName,
        serverKey: op.serverKey || null,
        commitHash: op.commitHash,
        branch: op.branch,
        channel: op.channel || op.repoName,
        timestamp: ts
      })
      break
    }

    case 'ref-update': {
      // Store latest ref value per repo+ref path.
      // Autobase linearization ensures last-writer-wins semantics.
      // Federation peers watch this key to know when to sync.
      const refKey = `ref:${op.repoName}:${op.ref}`
      const existing = await batch.get(refKey)
      const existingVersion = existing?.value?.version || 0

      // Only apply if version is higher (monotonic — prevents stale reverts)
      if (op.version > existingVersion) {
        await batch.put(refKey, {
          repoName: op.repoName,
          ref: op.ref,
          sha: op.sha,
          version: op.version,
          serverKey: op.serverKey || null,
          timestamp: op.timestamp || 0
        })
      }
      break
    }

    case 'add-writer': {
      if (op.writerKey) {
        try {
          await base.addWriter(Buffer.from(op.writerKey, 'hex'), { indexer: true })
        } catch (err) {
          console.error(`[Deploy:apply] add-writer failed for ${op.writerKey.slice(0, 16)}...: ${err.message}`)
        }
      }
      break
    }
  }
}