hbs2/hbs2-git/git-hbs2/Main.hs

281 lines
8.7 KiB
Haskell
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{-# Language UndecidableInstances #-}
module Main where
import HBS2.Git.Client.Prelude hiding (info)
import HBS2.Git.Client.App
import HBS2.Git.Client.Export
import HBS2.Git.Client.Import
import HBS2.Git.Client.State
import HBS2.Git.Data.RefLog
import HBS2.Git.Local.CLI qualified as Git
import HBS2.Git.Data.Tx.Git qualified as TX
import HBS2.Git.Data.Tx.Git (RepoHead(..))
import HBS2.Git.Data.Tx.Index
import HBS2.Git.Data.LWWBlock
import HBS2.Peer.Proto.RefChan.Types
import HBS2.Git.Data.GK
import HBS2.KeyMan.Keys.Direct
import HBS2.Storage.Operations.ByteString
import Data.HashSet qualified as HS
import Data.Maybe
import Data.Coerce
import Options.Applicative as O
import Data.ByteString.Lazy qualified as LBS
import Streaming.Prelude qualified as S
import System.Exit
globalOptions :: Parser [GitOption]
globalOptions = do
t <- flag [] [GitTrace]
( long "trace" <> short 't' <> help "allow trace"
)
d <- flag [] [GitDebug]
( long "debug" <> short 'd' <> help "allow debug"
)
pure (t <> d)
commands :: GitPerks m => Parser (GitCLI m ())
commands =
hsubparser ( command "export" (info pExport (progDesc "export repo to hbs2-git"))
<> command "import" (info pImport (progDesc "import repo from reflog"))
<> command "key" (info pKey (progDesc "key management"))
<> command "track" (info pTrack (progDesc "track tools"))
<> command "tools" (info pTools (progDesc "misc tools"))
)
pRefLogId :: ReadM RefLogId
pRefLogId = maybeReader (fromStringMay @RefLogId)
pRefChanId :: ReadM GitRefChanId
pRefChanId = maybeReader (fromStringMay @GitRefChanId)
pLwwKey :: ReadM (LWWRefKey 'HBS2Basic)
pLwwKey = maybeReader fromStringMay
pHashRef :: ReadM HashRef
pHashRef = maybeReader (fromStringMay @HashRef)
pInit :: GitPerks m => Parser (GitCLI m ())
pInit = do
pure runDefault
pExport :: GitPerks m => Parser (GitCLI m ())
pExport = do
puk <- argument pLwwKey (metavar "REFLOG-KEY")
et <- flag ExportInc ExportNew
( long "new" <> help "new is usable to export to a new empty reflog"
)
enc <- flag' ExportPublic (long "public" <> help "create unencrypted reflog")
<|>
( ExportPrivate <$>
strOption (long "encrypted" <> help "create encrypted reflog"
<> metavar "GROUP-KEY-FILE")
)
pure do
git <- Git.gitDir >>= orThrowUser "not a git dir"
notice (green "git dir" <+> pretty git <+> pretty (AsBase58 puk))
env <- ask
withGitEnv ( env & set gitApplyHeads False & set gitExportType et & set gitExportEnc enc) do
unless (et == ExportNew) do
importRepoWait puk
export puk mempty
pImport :: GitPerks m => Parser (GitCLI m ())
pImport = do
puk <- argument pLwwKey (metavar "LWWREF")
pure do
git <- Git.gitDir >>= orThrowUser "not a git dir"
importRepoWait puk
pTools :: GitPerks m => Parser (GitCLI m ())
pTools = hsubparser ( command "dump-pack" (info pDumpPack (progDesc "dump hbs2 git pack"))
<> command "show-ref" (info pShowRef (progDesc "show current references"))
<> command "show-remotes" (info pShowLww (progDesc "show current remotes (hbs2 references)"))
)
data DumpOpt = DumpInfoOnly | DumpObjects | DumpPack
pDumpPack :: GitPerks m => Parser (GitCLI m ())
pDumpPack = do
what <- dumpInfoOnly <|> dumpObjects <|> dumpPack
pure do
co <- liftIO LBS.getContents
(idSize,idVer,sidx,pack) <- TX.unpackPackMay co
& orThrowUser "can't unpack the bundle"
case what of
DumpInfoOnly -> do
liftIO $ print $ pretty "version:" <+> pretty idVer <> line
<> "index size:" <+> pretty idSize <> line
<> "objects:" <+> pretty (length sidx)
DumpObjects -> do
liftIO $ print $ vcat (fmap pretty sidx)
DumpPack -> do
liftIO $ LBS.putStr pack
where
dumpInfoOnly = flag DumpInfoOnly DumpInfoOnly
( long "info-only" )
dumpObjects = flag DumpObjects DumpObjects
( long "objects" )
dumpPack = flag DumpPack DumpPack
( long "pack" )
pShowLww :: GitPerks m => Parser (GitCLI m ())
pShowLww = pure do
items <- withState selectAllLww
liftIO $ print $ vcat (fmap fmt items)
where
fmt (l,n,k) = fill 4 (pretty n) <+> fill 32 (pretty l) <+> fill 32 (pretty (AsBase58 k))
pShowRef :: GitPerks m => Parser (GitCLI m ())
pShowRef = do
pure do
sto <- asks _storage
void $ runMaybeT do
tx <- withState do
selectMaxAppliedTx >>= lift . toMPlus <&> fst
rh <- TX.readRepoHeadFromTx sto tx >>= toMPlus
liftIO $ print $ vcat (fmap formatRef (_repoHeadRefs rh))
pKey :: GitPerks m => Parser (GitCLI m ())
pKey = hsubparser ( command "show" (info pKeyShow (progDesc "show current key"))
<> command "update" (info pKeyUpdate (progDesc "update current key"))
)
<|> pKeyShow
pKeyShow :: GitPerks m => Parser (GitCLI m ())
pKeyShow = do
full <- flag False True (long "full" <> help "show full key info")
pure do
sto <- asks _storage
void $ runMaybeT do
tx <- withState do
selectMaxAppliedTx >>= lift . toMPlus <&> fst
rh <- TX.readRepoHeadFromTx sto tx
>>= toMPlus
gkh <- toMPlus (_repoHeadGK0 rh)
if not full then do
liftIO $ print $ pretty gkh
else do
gk <- runExceptT (readGK0 sto gkh) >>= toMPlus
liftIO $ print $ ";; group key" <+> pretty gkh <> line <> line <> pretty gk
pKeyUpdate :: GitPerks m => Parser (GitCLI m ())
pKeyUpdate = do
rlog <- argument pRefLogId (metavar "REFLOG-KEY")
fn <- strArgument (metavar "GROUP-KEY-FILE")
pure do
gk <- loadGK0FromFile fn
`orDie` "can not load group key or invalid format"
sto <- asks _storage
gh <- writeAsMerkle sto (serialise gk) <&> HashRef
added <- withState $ runMaybeT do
(tx,_) <- lift selectMaxAppliedTx >>= toMPlus
lift do
insertNewGK0 rlog tx gh
commitAll
pure gh
case added of
Nothing -> liftIO $ putStrLn "not added" >> exitFailure
Just x -> liftIO $ print $ pretty x
pTrack :: GitPerks m => Parser (GitCLI m ())
pTrack = hsubparser ( command "send-repo-notify" (info pSendRepoNotify (progDesc "sends repository notification"))
)
pSendRepoNotify :: GitPerks m => Parser (GitCLI m ())
pSendRepoNotify = do
dry <- flag False True (short 'n' <> long "dry" <> help "don't post anything")
notifyChan <- argument pRefChanId (metavar "CHANNEL-KEY")
pure do
notice $ "test send-repo-notify" <+> pretty (AsBase58 notifyChan)
-- откуда мы берём ссылку, которую постим? их много.
lwws <- withState selectAllLww
-- берём те, для которых у нас есть приватный ключ (наши)
creds <- catMaybes <$> runKeymanClient do
for lwws $ \(lwref,_,_) -> do
loadCredentials (coerce @_ @(PubKey 'Sign 'HBS2Basic) lwref)
sto <- getStorage
rchanAPI <- asks _refChanAPI
hd <- getRefChanHead @L4Proto sto (RefChanHeadKey notifyChan)
`orDie` "refchan head not found"
let notifiers = view refChanHeadNotifiers hd & HS.toList
-- откуда мы берём ключ, которым подписываем?
-- ищем тоже в кеймане, берём тот, у которого выше weight
foundKey <- runKeymanClient (
S.head_ do
for notifiers $ \n -> do
lift (loadCredentials n) >>= maybe none S.yield
) `orDie` "signing key not found"
for_ creds $ \c -> do
let lww = LWWRefKey @'HBS2Basic (view peerSignPk c)
let lwwSk = view peerSignSk c
let tx = makeNotificationTx @'HBS2Basic (NotifyCredentials foundKey) lww lwwSk Nothing
notice $ "about to publish lwwref index entry:"
<+> pretty (AsBase58 $ view peerSignPk c)
-- как мы постим ссылку
unless dry do
void $ callService @RpcRefChanNotify rchanAPI (notifyChan, tx)
-- кто парсит ссылку и помещает в рефчан
main :: IO ()
main = do
(o, action) <- customExecParser (prefs showHelpOnError) $
O.info (liftA2 (,) globalOptions commands <**> helper)
( fullDesc
<> header "hbs2-git"
<> progDesc "hbs2-git"
)
runGitCLI o action