Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Build info #9

Merged
merged 5 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version = 3.7.14
version = 3.8.4
runner.dialect = scala3

indent.main = 2
Expand Down
4 changes: 2 additions & 2 deletions alpasso/src/main/scala/alpasso/cli/ArgParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ enum RemoteOp:
case Sync

enum RepoOp:
case Init(path: Option[Path], cypher: CypherAlg)
case Init(path: Path, cypher: CypherAlg)
case List
case Switch(index: Int)
case Log
Expand All @@ -34,7 +34,7 @@ object ArgParser:
val repos: Opts[Action] = Opts.subcommand("repo", "Repositories ops") {

val init = Opts.subcommand("init", "Init new repository") {
val path = Opts.option[Path]("path", "Repository path", "p").orNone
val path = Opts.option[Path]("path", "Repository path", "p")
val gpg = Opts.option[String]("gpg-fingerprint", "GPG fingerprint").map(CypherAlg.Gpg(_))

(path, gpg).mapN(RepoOp.Init.apply)
Expand Down
13 changes: 8 additions & 5 deletions alpasso/src/main/scala/alpasso/cli/CliApp.scala
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ object CliApp extends IOApp:

def provideConfig[A](f: RepositoryConfiguration => IO[Result[A]]): IO[Result[A]] =
(for
session <- EitherT.fromOptionF(smgr.current(), Err.UseSwitchCommand)
session <- EitherT.fromOptionF(smgr.current, Err.UseSwitchCommand)
cfg <- rmr.read(session.path).liftE[Err]
_ <- EitherT.cond(cfg.version == SemVer.current, (), Err.VersionMismatch(cfg.version))
result <- f(RepositoryConfiguration(session.path, cfg.version, cfg.cryptoAlg)).liftE[Err]
yield result).value

Expand All @@ -57,13 +58,15 @@ object CliApp extends IOApp:
case Right(Action.Repo(ops)) =>
ops match
case RepoOp.Init(pathOpt, cypher) =>
val path = pathOpt.getOrElse(Path.of(".local")).toAbsolutePath
(bootstrap[IO](path, SemVer.zero, cypher) <* smgr.setup(Session(path))) >>= handle
val path = pathOpt.toAbsolutePath
val boot = bootstrap[IO](path, SemVer.current, cypher)
.flatTap(_.fold(_ => IO.unit, _ => smgr.setup(Session(path))))
boot >>= handle

case RepoOp.List => smgr.listAll().map(_.into().asRight[Err]) >>= handle
case RepoOp.List => smgr.listAll.map(_.into().asRight[Err]) >>= handle
case RepoOp.Log => provideConfig(historyLog) >>= handle
case RepoOp.Switch(sel) =>
val switch = OptionT(smgr.listAll().map(_.zipWithIndex.find((_, idx) => idx == sel)))
val switch = OptionT(smgr.listAll.map(_.zipWithIndex.find((_, idx) => idx == sel)))
.cataF(
IO(Err.UseSwitchCommand.asLeft),
(s, _) => smgr.setup(s).as(s.into().asRight[Err])
Expand Down
15 changes: 10 additions & 5 deletions alpasso/src/main/scala/alpasso/cli/SessionManager.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,24 @@ import io.circe.syntax.given
case class Session(path: Path)

trait SessionManager[F[_]]:
def current(): F[Option[Session]]
def listAll(): F[List[Session]]
def current: F[Option[Session]]

def listAll: F[List[Session]]
def setup(session: Session): F[Unit]

object SessionManager:

def make[F[_]: Sync as S]: SessionManager[F] = new SessionManager[F]:
def make[F[_] : Sync]: SessionManager[F] =
Impl[F]

class Impl[F[_] : Sync as S] extends SessionManager[F]:
import S.blocking

private def sessionDir = {
val str = System.getProperty("user.home")
Path.of(str).toAbsolutePath.resolve(".alpasso")
}

private val sessionFile = sessionDir.resolve("sessions")

val empty = SessionData(current = None, sessions = Nil)
Expand All @@ -54,15 +59,15 @@ object SessionManager:
def modify(f: SessionData => SessionData): F[Unit] =
OptionT(readData()).cata(f(empty), f) >>= write

override def listAll(): F[List[Session]] =
override def listAll: F[List[Session]] =
readData().map(_.map(_.sessions).getOrElse(Nil))

override def setup(session: Session): F[Unit] =
modify(old =>
SessionData(current = session.some, sessions = (session :: old.sessions).distinct)
)

override def current(): F[Option[Session]] =
override def current: F[Option[Session]] =
readData().map(_.flatMap(_.current))

end SessionManager
Expand Down
45 changes: 23 additions & 22 deletions alpasso/src/main/scala/alpasso/cmdline/Command.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import alpasso.common.{ Logger, RawPackage, Result, SemVer }
import alpasso.core.model.*
import alpasso.service.cypher.*
import alpasso.service.fs.*
import alpasso.service.fs.RepoMetaErr.{ InvalidFormat, NotInitialized }
import alpasso.service.fs.RepositoryErr.fromGitError
import alpasso.service.fs.model.{ Branch, * }
import alpasso.service.git.{ GitError, GitRepo }

Expand All @@ -28,44 +30,43 @@ enum Err:
case InternalErr
case CommandSyntaxError(help: String)
case UseSwitchCommand
case VersionMismatch(version: SemVer)
case RepositoryProvisionErr(err: ProvisionErr)

object Err:
given Upcast[Err, RepositoryErr] = Err.SecretRepoErr(_)
given Upcast[Err, RepoMetaErr] = _ => Err.InternalErr
given Upcast[Err, ProvisionErr] = e => Err.RepositoryProvisionErr(e)
given Upcast[Err, CypherError] = e => Err.SecretRepoErr(e.upcast)

given Upcast[Err, RepoMetaErr] =
case NotInitialized(path) => Err.StorageNotInitialized(path)
case InvalidFormat(path) => Err.StorageCorrupted(path)
given Upcast[Err, ProvisionErr] = e => Err.RepositoryProvisionErr(e)
given Upcast[Err, CypherError] = e => Err.SecretRepoErr(e.upcast)

given Upcast[Err, GitError] =
ge => summon[Upcast[Err, RepositoryErr]].upcast(fromGitError(ge)) // todo fix it
end Err

def bootstrap[F[_]: Sync: Logger](repoDir: Path, version: SemVer, cypher: CypherAlg): F[Result[StorageView]] =
def bootstrap[F[_] : {Sync, Logger}](
repoDir: Path,
version: SemVer,
cypher: CypherAlg): F[Result[StorageView]] =
val provisioner = RepositoryProvisioner.make(repoDir)
val config = RepositoryMetaConfig(version, cypher)
provisioner.provision(config).liftE[Err].map(_ => StorageView(repoDir, cypher)).value

def historyLog[F[_]: Sync](configuration: RepositoryConfiguration): F[Result[HistoryLogView]] =
GitRepo.openExists(configuration.repoDir).use { git =>
import RepositoryErr.*
given Upcast[Err, GitError] =
ge => summon[Upcast[Err, RepositoryErr]].upcast(fromGitError(ge)) // todo fix it

git.history().nested.map(v => HistoryLogView.from(v.commits)).value.liftE[Err].value
}

def setupRemote[F[_]: Sync](name: String, url: String)(configuration: RepositoryConfiguration): F[Result[Unit]] =
GitRepo.openExists(configuration.repoDir).use { git =>
import RepositoryErr.*
given Upcast[Err, GitError] =
ge => summon[Upcast[Err, RepositoryErr]].upcast(fromGitError(ge)) // todo fix it

git.addRemote(name, url).liftE[Err].value
}
def setupRemote[F[_] : Sync](
name: String,
url: String
)(configuration: RepositoryConfiguration): F[Result[Unit]] =
GitRepo.openExists(configuration.repoDir).use { git => git.addRemote(name, url).liftE[Err].value }

def syncRemote[F[_]: Sync](configuration: RepositoryConfiguration): F[Result[Unit]] =
GitRepo.openExists(configuration.repoDir).use { git =>
import RepositoryErr.*
given Upcast[Err, GitError] =
ge => summon[Upcast[Err, RepositoryErr]].upcast(fromGitError(ge)) // todo fix it

val result = for
_ <- git.pullRemote().liftE[Err]
_ <- git.pushToRemote().liftE[Err]
Expand All @@ -91,15 +92,15 @@ trait Command[F[_]]:

object Command:

def make[F[_]: Async: Logger](config: RepositoryConfiguration): Command[F] =
def make[F[_] : {Async, Logger}](config: RepositoryConfiguration): Command[F] =
val cs = config.cypherAlg match
case CypherAlg.Gpg(fingerprint) => CypherService.gpg(fingerprint)

val reader = RepositoryReader.make(config, cs)
val mutator = RepositoryMutator.make(config)
Impl[F](cs, reader, mutator)

private class Impl[F[_]: Async: Logger](
private class Impl[F[_] : {Async, Logger}](
cs: CypherService[F],
reader: RepositoryReader[F],
mutator: RepositoryMutator[F])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import alpasso.service.fs.{ ProvisionErr, RepositoryErr }
case class ErrorView(error: String, suggest: Option[String] = None)

object ErrorView:

given Show[ErrorView] = Show.show(s => s"${s.error} ${s.suggest.getOrElse("")}")

given Converter[Err, ErrorView] =
Expand Down
14 changes: 10 additions & 4 deletions alpasso/src/main/scala/alpasso/common/SemVer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,28 @@ package alpasso.common
import cats.*
import cats.syntax.all.*

import alpasso.common.build.*

import io.circe.*

case class SemVer(major: Int, minor: Int, patch: Int)

object SemVer:
val zero: SemVer = SemVer(0, 0, 0)

def current: SemVer = fromString(BuildInfo.version).toOption.get

given Show[SemVer] = Show.show(v => s"${v.major}.${v.minor}.${v.patch}")
given Encoder[SemVer] = Encoder.encodeString.contramap(v => s"${v.major}.${v.minor}.${v.patch}")

private val parts = """(\d+)\.(\d+)\.(\d+)""".r

given Decoder[SemVer] = Decoder.decodeString.emap {
case parts(ma, mi, p) => SemVer(ma.toInt, mi.toInt, p.toInt).asRight
case _ => "mismatch semver format".asLeft
}
def fromString(version: String): Either[String, SemVer] =
version match
case parts(ma, mi, p) => SemVer(ma.toInt, mi.toInt, p.toInt).asRight
case _ => "mismatch semver format".asLeft

given Decoder[SemVer] = Decoder.decodeString.emap(fromString)

given Ordering[SemVer] =
(x: SemVer, y: SemVer) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ object CypherService:
override def encrypt(raw: Array[Byte]): F[Result[Array[Byte]]] = raw.asRight.pure
override def decrypt(raw: Array[Byte]): F[Result[Array[Byte]]] = raw.asRight.pure

def gpg[F[_]: Sync: Logger](fg: String): CypherService[F] = GpgImpl[F](fg)
def gpg[F[_] : {Sync, Logger}](fg: String): CypherService[F] = GpgImpl[F](fg)

@main
def main(): Unit = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ object RepositoryProvisioner:

val repoMetadataFile: String = ".alpasso"

def make[F[_]: Logger: Sync](repoDir: Path): Provisioner[F] =
def make[F[_] : {Logger, Sync}](repoDir: Path): Provisioner[F] =
val alg = MetaProvisioner(repoDir)

val gitted: Provisioner[Mid[F, *]] = GitProvisioner[F](repoDir)
Expand All @@ -49,7 +49,7 @@ object RepositoryProvisioner:

(cs |+| gitted) attach alg

class CypherProvisioner[F[_]: Sync: Logger] extends Provisioner[Mid[F, *]] {
class CypherProvisioner[F[_] : {Sync, Logger}] extends Provisioner[Mid[F, *]] {

override def provision(config: RepositoryMetaConfig): Mid[F, Either[ProvisionErr, Unit]] = {
action =>
Expand Down Expand Up @@ -77,7 +77,7 @@ object RepositoryProvisioner:
}
}

class LoggingProvisioner[F[_]: Monad: Logger] extends Provisioner[Mid[F, *]]:
class LoggingProvisioner[F[_] : {Monad, Logger}] extends Provisioner[Mid[F, *]]:

override def provision(config: RepositoryMetaConfig): Mid[F, Either[ProvisionErr, Unit]] =
action =>
Expand Down Expand Up @@ -105,20 +105,21 @@ object RepositoryProvisioner:
}

enum RepoMetaErr:
case NotInitialized, InvalidFormat
case NotInitialized(path: Path)
case InvalidFormat(path: Path)

object RepositoryConfigReader:

def make[F[_]: Sync as S: Logger]: RepositoryConfigReader[F] = (repoDir: Path) =>
def make[F[_] : {Sync as S, Logger}]: RepositoryConfigReader[F] = (repoDir: Path) =>
import S.blocking

val fullPath = repoDir.resolve(RepositoryProvisioner.repoMetadataFile)

blocking(Files.exists(fullPath)).flatMap { exists =>
if !exists then RepoMetaErr.NotInitialized.asLeft.pure[F]
if !exists then RepoMetaErr.NotInitialized(fullPath).asLeft.pure[F]
else
for
raw <- blocking(Files.readString(fullPath))
ctx <- blocking(parser.parse(raw).flatMap(_.as[RepositoryMetaConfig]))
yield ctx.leftMap(_ => RepoMetaErr.InvalidFormat)
yield ctx.leftMap(_ => RepoMetaErr.InvalidFormat(fullPath))
}
Loading
Loading