Skip to content

Commit

Permalink
Sync to/from file
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisPenner committed Jan 15, 2025
1 parent 4324c53 commit d95114b
Show file tree
Hide file tree
Showing 34 changed files with 1,533 additions and 28 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ dist-newstyle
*.prof.html
*.hp
*.ps
*.profiterole.*
/.direnv/
/.envrc

Expand Down
3 changes: 2 additions & 1 deletion codebase2/codebase-sqlite/U/Codebase/Sqlite/Branch/Format.hs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ data BranchLocalIds' t d p c = LocalIds
branchPatchLookup :: Vector p,
branchChildLookup :: Vector c
}
deriving (Show)
deriving (Show, Eq)

-- | Bytes encoding a LocalBranch
newtype LocalBranchBytes = LocalBranchBytes ByteString
Expand All @@ -110,6 +110,7 @@ newtype LocalBranchBytes = LocalBranchBytes ByteString
data SyncBranchFormat' parent text defn patch child
= SyncFull (BranchLocalIds' text defn patch child) LocalBranchBytes
| SyncDiff parent (BranchLocalIds' text defn patch child) LocalBranchBytes
deriving (Eq, Show)

type SyncBranchFormat = SyncBranchFormat' BranchObjectId TextId ObjectId PatchObjectId (BranchObjectId, CausalHashId)

Expand Down
1 change: 1 addition & 0 deletions codebase2/codebase-sqlite/U/Codebase/Sqlite/Causal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@ data SyncCausalFormat' causalHash valueHash = SyncCausalFormat
{ valueHash :: valueHash,
parents :: Vector causalHash
}
deriving stock (Eq, Show)

type SyncCausalFormat = SyncCausalFormat' CausalHashId BranchHashId
2 changes: 2 additions & 0 deletions codebase2/codebase-sqlite/U/Codebase/Sqlite/Decl/Format.hs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,11 @@ type SyncDeclFormat =

data SyncDeclFormat' t d
= SyncDecl (SyncLocallyIndexedComponent' t d)
deriving stock (Eq, Show)

newtype SyncLocallyIndexedComponent' t d
= SyncLocallyIndexedComponent (Vector (LocalIds' t d, ByteString))
deriving stock (Eq, Show)

-- [OldDecl] ==map==> [NewDecl] ==number==> [(NewDecl, Int)] ==sort==> [(NewDecl, Int)] ==> permutation is map snd of that

Expand Down
1 change: 1 addition & 0 deletions codebase2/codebase-sqlite/U/Codebase/Sqlite/Entity.hs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ data SyncEntity' text hash defn patch branchh branch causal
| N (Namespace.SyncBranchFormat' branch text defn patch (branch, causal))
| P (Patch.SyncPatchFormat' patch text hash defn)
| C (Causal.SyncCausalFormat' causal branchh)
deriving stock (Eq, Show)

entityType :: SyncEntity' text hash defn patch branchh branch causal -> TempEntityType
entityType = \case
Expand Down
2 changes: 1 addition & 1 deletion codebase2/codebase-sqlite/U/Codebase/Sqlite/LocalIds.hs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ data LocalIds' t h = LocalIds
{ textLookup :: Vector t,
defnLookup :: Vector h
}
deriving (Functor, Show)
deriving stock (Functor, Show, Eq)

type LocalIds = LocalIds' TextId ObjectId

Expand Down
2 changes: 2 additions & 0 deletions codebase2/codebase-sqlite/U/Codebase/Sqlite/Patch/Format.hs
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,15 @@ data PatchLocalIds' t h d = LocalIds
patchHashLookup :: Vector h,
patchDefnLookup :: Vector d
}
deriving stock (Eq, Show)

type SyncPatchFormat = SyncPatchFormat' PatchObjectId TextId HashId ObjectId

data SyncPatchFormat' parent text hash defn
= SyncFull (PatchLocalIds' text hash defn) ByteString
| -- | p is the identity of the thing that the diff is relative to
SyncDiff parent (PatchLocalIds' text hash defn) ByteString
deriving stock (Eq, Show)

-- | Apply a list of patch diffs to a patch, left to right.
applyPatchDiffs :: Patch -> [PatchDiff] -> Patch
Expand Down
2 changes: 2 additions & 0 deletions codebase2/codebase-sqlite/U/Codebase/Sqlite/Term/Format.hs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ newtype LocallyIndexedComponent' t d = LocallyIndexedComponent

newtype SyncLocallyIndexedComponent' t d
= SyncLocallyIndexedComponent (Vector (LocalIds' t d, ByteString))
deriving stock (Eq, Show)

{-
message = "hello, world" -> ABT { ... { Term.F.Text "hello, world" } } -> hashes to (#abc, 0)
Expand Down Expand Up @@ -127,6 +128,7 @@ data TermFormat' t d = Term (LocallyIndexedComponent' t d)
type SyncTermFormat = SyncTermFormat' TextId ObjectId

data SyncTermFormat' t d = SyncTerm (SyncLocallyIndexedComponent' t d)
deriving stock (Eq, Show)

data WatchResultFormat
= WatchResult WatchLocalIds Term
Expand Down
2 changes: 1 addition & 1 deletion codebase2/codebase-sqlite/unison-codebase-sqlite.cabal
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cabal-version: 1.12

-- This file has been generated from package.yaml by hpack version 0.36.0.
-- This file has been generated from package.yaml by hpack version 0.37.0.
--
-- see: https://github.com/sol/hpack

Expand Down
3 changes: 3 additions & 0 deletions hie.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ cradle:
- path: "unison-share-api/src"
component: "unison-share-api:lib"

- path: "unison-share-api/tests"
component: "unison-share-api:test:unison-share-api-tests"

- path: "unison-share-projects-api/src"
component: "unison-share-projects-api:lib"

Expand Down
1 change: 1 addition & 0 deletions lib/unison-sqlite/src/Unison/Sqlite.hs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ module Unison.Sqlite
Transaction,
runTransaction,
runTransactionWithRollback,
runTransactionExceptT,
runReadOnlyTransaction,
runWriteTransaction,
cacheTransaction,
Expand Down
9 changes: 9 additions & 0 deletions lib/unison-sqlite/src/Unison/Sqlite/Transaction.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module Unison.Sqlite.Transaction
Transaction,
runTransaction,
runTransactionWithRollback,
runTransactionExceptT,
runReadOnlyTransaction,
runWriteTransaction,
cacheTransaction,
Expand Down Expand Up @@ -44,6 +45,7 @@ where

import Control.Concurrent (threadDelay)
import Control.Exception (Exception (fromException), onException, throwIO)
import Control.Monad.Trans.Except (ExceptT, runExceptT)
import Control.Monad.Trans.Reader (ReaderT (..))
import Data.Text qualified as Text
import Data.Unique (Unique, newUnique)
Expand Down Expand Up @@ -130,6 +132,13 @@ runTransactionWithRollback conn transaction = liftIO do
Right x -> pure x
{-# SPECIALIZE runTransactionWithRollback :: Connection -> ((forall void. a -> Transaction void) -> Transaction a) -> IO a #-}

-- | Run a transaction wrapped in an 'ExceptT'. If the ExceptT fails, the transaction is rolled back.
runTransactionExceptT :: (MonadIO m, HasCallStack) => Connection -> ExceptT e Transaction a -> m (Either e a)
runTransactionExceptT conn transaction = runTransactionWithRollback conn \rollback -> do
runExceptT transaction >>= \case
Left e -> rollback (Left e)
Right a -> pure (Right a)

-- | Run a transaction that is known to only perform reads.
--
-- The action is provided a function that peels off the 'Transaction' newtype without sending the corresponding
Expand Down
10 changes: 10 additions & 0 deletions parser-typechecker/src/Unison/Codebase.hs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ module Unison.Codebase
-- * Direct codebase access
runTransaction,
runTransactionWithRollback,
runTransactionExceptT,
withConnection,
withConnectionIO,

Expand All @@ -112,6 +113,7 @@ module Unison.Codebase
)
where

import Control.Monad.Except (ExceptT)
import Data.Map qualified as Map
import Data.Set qualified as Set
import U.Codebase.Branch qualified as V2Branch
Expand Down Expand Up @@ -174,6 +176,14 @@ runTransactionWithRollback ::
runTransactionWithRollback Codebase {withConnection} action =
withConnection \conn -> Sqlite.runTransactionWithRollback conn action

runTransactionExceptT ::
(MonadIO m) =>
Codebase m v a ->
ExceptT e Sqlite.Transaction b ->
m (Either e b)
runTransactionExceptT Codebase {withConnection} action =
withConnection \conn -> Sqlite.runTransactionExceptT conn action

getShallowCausalAtPathFromRootHash ::
-- Causal to start at, if Nothing use the codebase's root branch.
CausalHash ->
Expand Down
6 changes: 6 additions & 0 deletions unison-cli/package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ library:
- condition: "!os(windows)"
dependencies: unix
dependencies:
- attoparsec
- Diff
- IntervalMap
- ListLike
Expand All @@ -32,7 +33,10 @@ library:
- co-log-core
- code-page
- concurrent-output
- conduit
- containers >= 0.6.3
- conduit
- conduit-extra
- cryptonite
- either
- errors
Expand Down Expand Up @@ -65,8 +69,10 @@ library:
- recover-rtti
- regex-tdfa
- semialign
- serialise
- servant
- servant-client
- servant-conduit
- stm
- temporary
- text-ansi
Expand Down
8 changes: 8 additions & 0 deletions unison-cli/src/Unison/Cli/MonadUtils.hs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ module Unison.Cli.MonadUtils
stepManyAtM,
updateProjectBranchRoot,
updateProjectBranchRoot_,
setProjectBranchRootToCausalHash,
updateAtM,
updateAt,
updateAndStepAt,
Expand Down Expand Up @@ -447,6 +448,13 @@ updateProjectBranchRoot projectBranch reason f = do
Q.setProjectBranchHead reason (projectBranch ^. #projectId) (projectBranch ^. #branchId) causalHashId
pure result

setProjectBranchRootToCausalHash :: ProjectBranch -> Text -> CausalHash -> Cli ()
setProjectBranchRootToCausalHash projectBranch reason targetCH = do
Cli.time "setProjectBranchRootToCausalHash" do
Cli.runTransaction $ do
targetCHID <- Q.expectCausalHashIdByCausalHash targetCH
Q.setProjectBranchHead reason (projectBranch ^. #projectId) (projectBranch ^. #branchId) targetCHID

updateProjectBranchRoot_ :: ProjectBranch -> Text -> (Branch IO -> Branch IO) -> Cli ()
updateProjectBranchRoot_ projectBranch reason f = do
updateProjectBranchRoot projectBranch reason (\b -> pure (f b, ()))
Expand Down
19 changes: 18 additions & 1 deletion unison-cli/src/Unison/Codebase/Editor/HandleInput.hs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ import Unison.Codebase.Editor.HandleInput.DeleteBranch (handleDeleteBranch)
import Unison.Codebase.Editor.HandleInput.DeleteNamespace (getEndangeredDependents, handleDeleteNamespace)
import Unison.Codebase.Editor.HandleInput.DeleteProject (handleDeleteProject)
import Unison.Codebase.Editor.HandleInput.Dependents (handleDependents)
import Unison.Codebase.Editor.HandleInput.EditNamespace (handleEditNamespace)
import Unison.Codebase.Editor.HandleInput.EditDependents (handleEditDependents)
import Unison.Codebase.Editor.HandleInput.EditNamespace (handleEditNamespace)
import Unison.Codebase.Editor.HandleInput.FindAndReplace (handleStructuredFindI, handleStructuredFindReplaceI, handleTextFindI)
import Unison.Codebase.Editor.HandleInput.FormatFile qualified as Format
import Unison.Codebase.Editor.HandleInput.Global qualified as Global
Expand All @@ -87,6 +87,7 @@ import Unison.Codebase.Editor.HandleInput.ReleaseDraft (handleReleaseDraft)
import Unison.Codebase.Editor.HandleInput.Run (handleRun)
import Unison.Codebase.Editor.HandleInput.RuntimeUtils qualified as RuntimeUtils
import Unison.Codebase.Editor.HandleInput.ShowDefinition (handleShowDefinition)
import Unison.Codebase.Editor.HandleInput.SyncV2 qualified as SyncV2
import Unison.Codebase.Editor.HandleInput.TermResolution (resolveMainRef)
import Unison.Codebase.Editor.HandleInput.Tests qualified as Tests
import Unison.Codebase.Editor.HandleInput.Todo (handleTodo)
Expand Down Expand Up @@ -688,6 +689,17 @@ loop e = do
Cli.respond Success
PullI sourceTarget pullMode -> handlePull sourceTarget pullMode
PushRemoteBranchI pushRemoteBranchInput -> handlePushRemoteBranch pushRemoteBranchInput
SyncToFileI syncFileDest projectBranchName -> SyncV2.handleSyncToFile syncFileDest projectBranchName
SyncFromFileI syncFileSrc projectBranchName -> do
description <- inputDescription input
SyncV2.handleSyncFromFile description syncFileSrc projectBranchName
SyncFromCodebaseI srcCodebasePath srcBranch destBranch -> do
description <- inputDescription input
let srcBranch' =
srcBranch & over #project \case
Nothing -> error "todo"
Just proj -> proj
SyncV2.handleSyncFromCodebase description srcCodebasePath srcBranch' destBranch
ListDependentsI hq -> handleDependents hq
ListDependenciesI hq -> handleDependencies hq
NamespaceDependenciesI path -> handleNamespaceDependencies path
Expand Down Expand Up @@ -1012,6 +1024,11 @@ inputDescription input =
ProjectsI -> wat
PullI {} -> wat
PushRemoteBranchI {} -> wat
SyncToFileI {} -> wat
SyncFromFileI fp pab ->
pure $ "sync.from-file " <> into @Text fp <> " " <> into @Text pab
SyncFromCodebaseI fp srcBranch destBranch -> do
pure $ "sync.from-file " <> into @Text fp <> " " <> into @Text srcBranch <> " " <> into @Text destBranch
QuitI {} -> wat
ReleaseDraftI {} -> wat
ShowDefinitionI {} -> wat
Expand Down
69 changes: 69 additions & 0 deletions unison-cli/src/Unison/Codebase/Editor/HandleInput/SyncV2.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
module Unison.Codebase.Editor.HandleInput.SyncV2
( handleSyncToFile,
handleSyncFromFile,
handleSyncFromCodebase,
)
where

import Control.Lens
import Control.Monad.Reader (MonadReader (..))
import U.Codebase.Sqlite.Queries qualified as Q
import Unison.Cli.Monad (Cli)
import Unison.Cli.Monad qualified as Cli
import Unison.Cli.MonadUtils qualified as Cli
import Unison.Cli.ProjectUtils qualified as Project
import Unison.Codebase (CodebasePath)
import Unison.Codebase qualified as Codebase
import Unison.Codebase.Editor.Output qualified as Output
import Unison.Codebase.Init qualified as Init
import Unison.Codebase.SqliteCodebase qualified as SqliteCodebase
import Unison.Prelude
import Unison.Project (ProjectAndBranch (..), ProjectBranchName, ProjectName)
import Unison.Share.SyncV2 qualified as SyncV2
import Unison.SyncV2.Types (BranchRef)

handleSyncToFile :: FilePath -> ProjectAndBranch (Maybe ProjectName) (Maybe ProjectBranchName) -> Cli ()
handleSyncToFile destSyncFile branchToSync = do
pp <- Cli.getCurrentProjectPath
projectBranch <- Project.resolveProjectBranchInProject (pp ^. #project) branchToSync
causalHash <- Cli.runTransaction $ Project.getProjectBranchCausalHash (projectBranch ^. #branch)
let branchRef = into @BranchRef $ ProjectAndBranch (projectBranch ^. #project . #name) (projectBranch ^. #branch . #name)
Cli.Env {codebase} <- ask
liftIO (SyncV2.syncToFile codebase causalHash (Just branchRef) destSyncFile) >>= \case
Left err -> Cli.respond (Output.SyncPullError err)
Right _ -> pure ()

handleSyncFromFile :: Text -> FilePath -> ProjectAndBranch (Maybe ProjectName) ProjectBranchName -> Cli ()
handleSyncFromFile description srcSyncFile branchToSync = do
pp <- Cli.getCurrentProjectPath
projectBranch <- Project.resolveProjectBranchInProject (pp ^. #project) (over #branch Just branchToSync)
let shouldValidate = True
SyncV2.syncFromFile shouldValidate srcSyncFile >>= \case
Left err -> Cli.respond (Output.SyncPullError err)
Right causalHash -> do
Cli.setProjectBranchRootToCausalHash (projectBranch ^. #branch) description causalHash

handleSyncFromCodebase :: Text -> CodebasePath -> ProjectAndBranch ProjectName ProjectBranchName -> ProjectAndBranch (Maybe ProjectName) ProjectBranchName -> Cli ()
handleSyncFromCodebase description srcCodebasePath srcBranch destBranch = do
Cli.Env {codebase} <- ask
pp <- Cli.getCurrentProjectPath
projectBranch <- Project.resolveProjectBranchInProject (pp ^. #project) (over #branch Just destBranch)
r <- liftIO $ Init.withOpenCodebase SqliteCodebase.init "sync-src" srcCodebasePath Init.DontLock (Init.MigrateAfterPrompt Init.Backup Init.Vacuum) \srcCodebase -> do
Codebase.withConnection srcCodebase \srcConn -> do
maySrcCausalHash <- Codebase.runTransaction srcCodebase $ do
let ProjectAndBranch srcProjName srcBranchName = srcBranch
runMaybeT do
project <- MaybeT (Q.loadProjectByName srcProjName)
branch <- MaybeT (Q.loadProjectBranchByName (project ^. #projectId) srcBranchName)
lift $ Project.getProjectBranchCausalHash branch
case maySrcCausalHash of
Nothing -> pure $ Left (error "Todo proper error")
Just srcCausalHash -> do
let shouldValidate = True
fmap (const srcCausalHash) <$> liftIO (SyncV2.syncFromCodebase shouldValidate srcConn codebase srcCausalHash)

case r of
Left _err -> pure $ error "Todo proper error"
Right (Left syncErr) -> Cli.respond (Output.SyncPullError syncErr)
Right (Right causalHash) -> do
Cli.setProjectBranchRootToCausalHash (projectBranch ^. #branch) description causalHash
3 changes: 3 additions & 0 deletions unison-cli/src/Unison/Codebase/Editor/Input.hs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ data Input
| DiffNamespaceI BranchId2 BranchId2 -- old new
| PullI !PullSourceTarget !PullMode
| PushRemoteBranchI PushRemoteBranchInput
| SyncToFileI FilePath (ProjectAndBranch (Maybe ProjectName) (Maybe ProjectBranchName))
| SyncFromFileI FilePath UnresolvedProjectBranch
| SyncFromCodebaseI FilePath UnresolvedProjectBranch UnresolvedProjectBranch
| ResetI (BranchId2 {- namespace to reset it to -}) (Maybe UnresolvedProjectBranch {- ProjectBranch to reset -})
| -- | used in Welcome module to give directions to user
--
Expand Down
3 changes: 3 additions & 0 deletions unison-cli/src/Unison/Codebase/Editor/Output.hs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ import Unison.Share.Sync.Types qualified as Sync
import Unison.ShortHash (ShortHash)
import Unison.Symbol (Symbol)
import Unison.Sync.Types qualified as Share (DownloadEntitiesError, UploadEntitiesError)
import Unison.SyncV2.Types qualified as SyncV2
import Unison.Syntax.Parser qualified as Parser
import Unison.Term (Term)
import Unison.Type (Type)
Expand Down Expand Up @@ -440,6 +441,7 @@ data Output
| -- | A literal output message. Use this if it's too cumbersome to create a new Output constructor, e.g. for
-- ephemeral progress messages that are just simple strings like "Loading branch..."
Literal !(P.Pretty P.ColorText)
| SyncPullError (Sync.SyncError SyncV2.PullError)

data MoreEntriesThanShown = MoreEntriesThanShown | AllEntriesShown
deriving (Eq, Show)
Expand Down Expand Up @@ -678,6 +680,7 @@ isFailure o = case o of
IncoherentDeclDuringMerge {} -> True
IncoherentDeclDuringUpdate {} -> True
Literal _ -> False
SyncPullError {} -> True

isNumberedFailure :: NumberedOutput -> Bool
isNumberedFailure = \case
Expand Down
Loading

0 comments on commit d95114b

Please sign in to comment.