From 1cf8dab616bbafa466eb281ac7d1866daaf1e527 Mon Sep 17 00:00:00 2001
From: Reid 'arrdem' McKenzie <me@arrdem.com>
Date: Thu, 28 Jul 2022 19:24:17 -0600
Subject: [PATCH] Add a way to bust the log cache

---
 projects/cram/src/python/cram/__main__.py | 30 +++++++++++++----------
 1 file changed, 17 insertions(+), 13 deletions(-)

diff --git a/projects/cram/src/python/cram/__main__.py b/projects/cram/src/python/cram/__main__.py
index a4afd39..d89f405 100644
--- a/projects/cram/src/python/cram/__main__.py
+++ b/projects/cram/src/python/cram/__main__.py
@@ -6,7 +6,6 @@ import os
 from pathlib import Path
 import pickle
 from typing import List
-import sys
 
 from . import (
     __author__,
@@ -139,13 +138,15 @@ def simplify(old_fs: Vfs, new_fs: Vfs, /, exec_idempotent=True) -> Vfs:
         except ValueError:
             pass
 
-    # Dedupe the new log while preserving order
-    distinct = set()
-    for txn, idx in zip(new_fs._log, range(len(new_fs._log))):
-        if txn in distinct:
-            new_fs._log.pop(idx)
-        else:
-            distinct.add(txn)
+    # Dedupe the new log while preserving order.
+    keys = set()
+    deduped = []
+    for op in new_fs._log:
+        key = str(op)
+        if key not in keys:
+            keys.add(key)
+            deduped.append(op)
+    new_fs._log = deduped
 
     return new_fs
 
@@ -194,21 +195,20 @@ def cli():
 
 @cli.command("apply")
 @click.option("--execute/--dry-run", default=False)
+@click.option("--force/--no-force", default=False)
 @click.option("--state-file", default=".cram.log", type=Path)
 @click.option("--optimize/--no-optimize", default=True)
 @click.option("--require", type=str, multiple=True, default=[f"hosts.d/{os.uname()[1].split('.')[0]}", "profiles.d/default"])
 @click.option("--exec-idempotent/--exec-always", "exec_idempotent", default=True)
 @click.argument("confdir", type=Path)
 @click.argument("destdir", type=Path)
-def do_apply(confdir, destdir, state_file, execute, optimize, require, exec_idempotent):
+def do_apply(confdir, destdir, state_file, execute, optimize, force, require, exec_idempotent):
     """The entry point of cram."""
 
     # Resolve the two input paths to absolutes
     root = confdir.resolve()
     dest = destdir.resolve()
 
-    log.info(f"Installing requirements {require}")
-
     if not root.is_dir():
         log.fatal(f"{confdir} does not exist!")
         _exit(1)
@@ -216,8 +216,12 @@ def do_apply(confdir, destdir, state_file, execute, optimize, require, exec_idem
     if not state_file.is_absolute():
         state_file = root / state_file
 
-    old_fs = load_state(state_file)
-    log.debug(f"Loaded old state consisting of {len(old_fs._log)} steps")
+    if not force:
+        old_fs = load_state(state_file)
+        log.debug(f"Loaded old state consisting of {len(old_fs._log)} steps")
+    else:
+        # Force an empty state
+        old_fs = Vfs([])
 
     new_fs = build_fs(root, dest, require)
     log.debug(f"Built new state consisting of {len(new_fs._log)} steps")