on master help sign in
Add `ob internal verify-migration`

This currently ensures the all paths from $start (first commit) commutes. Need to do the same for all pairs now.

commita38303392a7a235e4b2f92556fb2f9c1a467a524 authorSridhar Ratnakumar <srid@srid.ca> dateFri 22 Jun 2018, 14:01 (8 years ago) parent7ac8c0f
7 files changed+65−26
lib/cliapp/src/Obelisk/CliApp/Spinner.hs+4−1 lib/command/src/Obelisk/Command.hs+11−9 lib/command/src/Obelisk/Command/Upgrade.hs+16−10 lib/command/src/Obelisk/Command/Upgrade/Hash.hs+1−1 lib/migration/src/Obelisk/Migration.hs+33−5 migration/73bcf3ed6593d074bd097cd43f5145d1-375acf7f2a3b3c487da2a6b30735d74d/obelisk-handoff migration/73bcf3ed6593d074bd097cd43f5145d1-375acf7f2a3b3c487da2a6b30735d74d/obelisk-upgrade
lib/cliapp/src/Obelisk/CliApp/Spinner.hs+4−1
@@ -136,8 +136,11 @@ type SpinnerTheme = [Text]
136136spinnerCircleHalves :: SpinnerTheme
137137spinnerCircleHalves = ["◐", "◓", "◑", "◒"]
138138
139+spinnerMoon :: SpinnerTheme
140+spinnerMoon = ["🌑", "🌒", "🌓", "🌔", "🌕", "🌖", "🌗", "🌘"]
141+
139142defaultSpinnerTheme :: SpinnerTheme
140−defaultSpinnerTheme = spinnerCircleHalves
143+defaultSpinnerTheme = spinnerMoon
141144
142145-- | Like `bracket` but the `release` function can know whether an exception was raised
143146bracket' :: MonadMask m => m a -> (a -> Maybe c -> m b) -> (a -> m c) -> m c
lib/command/src/Obelisk/Command.hs+11−9
@@ -100,6 +100,7 @@ data ObInternal
100100 = ObInternal_RunStaticIO StaticKey
101101 | ObInternal_Hash (Maybe Text)
102102 | ObInternal_CreateMigration
103+ | ObInternal_VerifyMigration
103104 | ObInternal_Migrate Hash
104105 | ObInternal_CLIDemo
105106 deriving Show
@@ -202,6 +203,7 @@ internalCommand = subparser $ mconcat
202203 [ command "run-static-io" $ info (ObInternal_RunStaticIO <$> argument (eitherReader decodeStaticKey) (action "static-key")) mempty
203204 , command "hash" $ info (ObInternal_Hash <$> argument (maybeReader $ Just . Just . T.pack) (action "rev" <> metavar "GITREVISION" <> value Nothing <> help "Calculate for the specified git revision (defaults to working copy")) $ progDesc "Computes hash of working directory (or git revision)"
204205 , command "create-migration" $ info (pure ObInternal_CreateMigration) mempty
206+ , command "verify-migration" $ info (pure ObInternal_VerifyMigration) mempty
205207 , command "migrate" $ info (ObInternal_Migrate <$> strArgument (action "fromHash" <> metavar "FROMHASH" <> help "Migrate from this hash")) $ progDesc "Perform a migrate from the given hash to HEAD of obelisk thunk"
206208 , command "clidemo" $ info (pure ObInternal_CLIDemo) mempty
207209 ]
@@ -393,16 +395,16 @@ ob = \case
393395 Just p -> do
394396 c <- getObelisk
395397 liftIO $ runObelisk c $ deRefStaticPtr p
396− ObInternal_Hash revM -> case revM of
397− Nothing -> do
398− hash <- getDirectoryHash [migrationDirName] "."
399− putLog Notice $ "Hash: " <> hash
400− Just rev -> do
401− [hash] <- getHashAtGitRevision [rev] [migrationDirName] "."
402− putLog Notice $ "Hash for " <> rev <> ": " <> hash
403− ObInternal_CreateMigration -> do
398+ ObInternal_Hash revM -> do
399+ hash <- case revM of
400+ Nothing -> getDirectoryHash [migrationDirName] "."
401+ Just rev -> fmap head $ getHashAtGitRevision [rev] [migrationDirName] "."
402+ putLog Notice hash
403+ ObInternal_CreateMigration ->
404404 createMigrationEdgeFromHEAD "."
405− ObInternal_Migrate fromHash -> do
405+ ObInternal_VerifyMigration ->
406+ verifyGraph "."
407+ ObInternal_Migrate fromHash ->
406408 migrateObelisk "." fromHash
407409 ObInternal_CLIDemo -> cliDemo
408410
lib/command/src/Obelisk/Command/Upgrade.hs+16−10
@@ -4,6 +4,7 @@
44{-# LANGUAGE MultiParamTypeClasses #-}
55{-# LANGUAGE OverloadedStrings #-}
66{-# LANGUAGE ScopedTypeVariables #-}
7+{-# LANGUAGE TypeApplications #-}
78module Obelisk.Command.Upgrade where
89
910import Control.Monad (forM, unless, void)
@@ -85,9 +86,7 @@ decideHandOffToProjectOb project = do
8586 Just m -> do
8687 -- We don't have ambient's ob source code, so locate its hash from the
8788 -- graph. The last vertex should be it.
88− ambientHash <- getLast (_migration_graph m) >>= \case
89− Nothing -> failWith "Ambient ob has no (last) vertex"
90− Just v -> pure v
89+ ambientHash <- getLast (_migration_graph m)
9190 unless (hasVertex ambientHash m) $
9291 failWith "Ambient ob's hash is not in its own graph"
9392 return (m, ambientHash)
@@ -166,6 +165,13 @@ migrateObelisk project fromHash = void $ withSpinner' "Migrating to new Obelisk"
166165 callProcessAndLogOutput (Notice, Notice) $ gitProc project ["checkout", impl]
167166
168167-- | Get the migration graph for project
168+getMigrationGraph
169+ :: (Action action, MonadObelisk m)
170+ => FilePath -> MigrationGraph -> m (Migration action)
171+getMigrationGraph obDir graph = getMigrationGraph' obDir graph >>= \case
172+ Nothing -> failWith "Migration graph missing"
173+ Just m -> pure m
174+
169175getMigrationGraph'
170176 :: (Action action, MonadObelisk m)
171177 => FilePath -> MigrationGraph -> m (Maybe (Migration action))
@@ -178,6 +184,12 @@ getMigrationGraph' obDir graph = do
178184computeVertexHash :: MonadObelisk m => FilePath -> m Hash
179185computeVertexHash = getDirectoryHash [migrationDirName]
180186
187+-- | Verify the integrity of the migration graph in relation to the Git repo.
188+verifyGraph :: MonadObelisk m => FilePath -> m ()
189+verifyGraph obDir = do
190+ upgradeGraph <- getMigrationGraph @Text obDir MigrationGraph_ObeliskUpgrade
191+ ensureGraphIntegrity upgradeGraph
192+
181193createMigrationEdgeFromHEAD :: MonadObelisk m => FilePath -> m ()
182194createMigrationEdgeFromHEAD obDir = do
183195 headHash <- getHeadVertex obDir
@@ -199,9 +211,7 @@ createMigrationEdgeFromHEAD obDir = do
199211-- Fail if the hash does not exist in project's migration graph.
200212getHeadVertex :: MonadObelisk m => FilePath -> m Hash
201213getHeadVertex obDir = do
202− projectGraph :: Migration Text <- getMigrationGraph' obDir MigrationGraph_ObeliskUpgrade >>= \case
203− Nothing -> failWith "No migration graph found"
204− Just g -> pure g
214+ projectGraph <- getMigrationGraph @Text obDir MigrationGraph_ObeliskUpgrade
205215 [headHash] <- getHashAtGitRevision ["HEAD"] migrationIgnore obDir
206216 unless (hasVertex headHash projectGraph) $ do
207217 -- This means that the HEAD commit has no vertex in the graph,
@@ -246,10 +256,6 @@ backfillGraph lastN obDir = do
246256 Just n -> take n xs
247257 Nothing -> xs
248258
249−-- | TODO: Verify the integrity of the migration graph in relation to the Git repo.
250−verifyGraph :: MonadObelisk m => FilePath -> m ()
251−verifyGraph = undefined
252−
253259migrationDir :: FilePath -> FilePath
254260migrationDir project = project </> migrationDirName
255261
lib/command/src/Obelisk/Command/Upgrade/Hash.hs+1−1
@@ -62,7 +62,7 @@ getDirectoryHashDestructive excludes dir = do
6262
6363getHashAtGitRevision :: MonadObelisk m => [Text] -> [FilePath] -> FilePath -> m [Hash]
6464getHashAtGitRevision revs excludes dir = withSystemTempDirectory "obelisk-hashrev-" $ \tmpDir -> do
65− withSpinner (T.pack $ "Copying " <> dir <> " to " <> tmpDir) $ do
65+ withSpinnerNoTrail (T.pack $ "Copying " <> dir <> " to " <> tmpDir) $ do
6666 runProc $ copyDir dir tmpDir
6767 tidyUpGitWorkingCopy tmpDir
6868 -- Discard changes to tracked files
lib/migration/src/Obelisk/Migration.hs+33−5
@@ -5,7 +5,7 @@
55{-# LANGUAGE TupleSections #-}
66module Obelisk.Migration where
77
8−import Control.Monad (forM, forM_, unless)
8+import Control.Monad (forM, forM_, unless, void)
99import Control.Monad.Catch (Exception, MonadThrow, throwM)
1010import Control.Monad.IO.Class (MonadIO, liftIO)
1111import Control.Monad.State.Strict (evalStateT, get, put)
@@ -40,6 +40,9 @@ data CyclicGraphError = CyclicGraphError
4040data MultipleTerminalVertices = MultipleTerminalVertices
4141 deriving Show
4242
43+data NoTerminalVertex = NoTerminalVertex
44+ deriving Show
45+
4346data NonEquivalentPaths = NonEquivalentPaths Hash Hash
4447 deriving Show
4548
@@ -48,6 +51,7 @@ data MissingEdgeInActionMap = MissingEdgeInActionMap (Hash, Hash)
4851
4952instance Exception CyclicGraphError
5053instance Exception MultipleTerminalVertices
54+instance Exception NoTerminalVertex
5155instance Exception NonEquivalentPaths
5256instance Exception MissingEdgeInActionMap
5357
@@ -85,6 +89,30 @@ readGraph root name = doesDirectoryExist root >>= \case
8589 False -> do
8690 return Nothing
8791
92+-- | Ensures that the migration class is correct
93+--
94+-- Throw the following if integrity check fails
95+-- * `CyclicGraphError`
96+-- * `NonEquivalentPaths`
97+ensureGraphIntegrity
98+ :: (MonadThrow m, Monoid action, Ord action)
99+ => Migration action -> m ()
100+ensureGraphIntegrity m = do
101+ graph <- fmap adjacencyMap $ getDag $ _migration_graph m
102+ firstVertex <- getFirst $ _migration_graph m
103+ let
104+ traverseFrom visited acc start = case Map.lookup start graph of
105+ Nothing -> pure ()
106+ Just adjs -> void $ flip traverse (Set.toList adjs) $ \adj -> do
107+ acc' <- fmap (mappend acc) $ getAction m (start, adj)
108+ visited' <- case Map.lookup adj visited of
109+ Nothing -> pure $ Map.insert adj acc' visited
110+ Just adjAcc -> if acc' == adjAcc
111+ then pure visited
112+ else throwM $ NonEquivalentPaths start adj
113+ traverseFrom visited' acc' adj
114+ traverseFrom mempty mempty firstVertex
115+
88116-- | Find the concataneted actions between two vertices
89117--
90118-- All paths between the vertices must have the same mappend'ed action;
@@ -135,10 +163,10 @@ getAction (Migration _ h) e = case Map.lookup e h of
135163-- | Get the last vertex of the given DAG. Nothing if empty graph.
136164--
137165-- Assumes that the graph is fully connected.
138−getLast :: (MonadThrow m, Ord a, Eq a) => AdjacencyMap a -> m (Maybe a)
166+getLast :: (MonadThrow m, Ord a, Eq a) => AdjacencyMap a -> m a
139167getLast g = lastVertices >>= \case
140− [] -> pure $ Nothing
141− [x] -> pure $ Just x
168+ [] -> throwM NoTerminalVertex
169+ [x] -> pure x
142170 _ -> throwM MultipleTerminalVertices
143171 where
144172 lastVertices
@@ -149,7 +177,7 @@ getLast g = lastVertices >>= \case
149177 =<< getDag g
150178
151179-- | Get the first vertex of the given DAG. Nothing if empty graph.
152−getFirst :: (MonadThrow m, Ord a, Eq a) => AdjacencyMap a -> m (Maybe a)
180+getFirst :: (MonadThrow m, Ord a, Eq a) => AdjacencyMap a -> m a
153181getFirst = getLast . transpose
154182
155183-- | Write an edge directly to the filesystem.
migration/73bcf3ed6593d074bd097cd43f5145d1-375acf7f2a3b3c487da2a6b30735d74d/obelisk-handoffnew file

No changes to the content.

migration/73bcf3ed6593d074bd097cd43f5145d1-375acf7f2a3b3c487da2a6b30735d74d/obelisk-upgradenew file

No changes to the content.