tools

various tools I have been using throughout the years
Log | Files | Refs | README | LICENSE

commit 15db339eec0f1c610ceb56196d203eb3b5798a2a
parent 238d7147e6e91099038ea347aa40f574b499c69d
Author: mtmn <miro@haravara.org>
Date:   Thu, 14 May 2026 17:59:21 +0200

chore: remove backup

Diffstat:
MREADME.md | 1-
Dbackup/.gitignore | 1-
Dbackup/README.md | 33---------------------------------
Dbackup/backup.cabal | 18------------------
Dbackup/backup.hs | 218-------------------------------------------------------------------------------
5 files changed, 0 insertions(+), 271 deletions(-)

diff --git a/README.md b/README.md @@ -4,7 +4,6 @@ Various tools I have been using throughout the years. | tool | description | |---|---| -| [backup](backup/) | terminal backup orchestrator | | [bandeno](bandeno/) | Bandcamp RSS feed proxy | | [diffamer](diffamer/) | line-file syncer with rsync and coloured diff | | [diggah](diggah/) | find files modified within a time range, locally or over SSH | diff --git a/backup/.gitignore b/backup/.gitignore @@ -1 +0,0 @@ -dist-* diff --git a/backup/README.md b/backup/README.md @@ -1,33 +0,0 @@ -# backup - -A backup orchestrator that commits and pushes git repos, snapshots directories with [plakar](https://github.com/PlakarKorp/plakar), and syncs files to S3 via `aws s3 sync`. - -## Building - -```sh -cabal build -``` - -## Usage - -```sh -backup [-t <target>] [--push] [--quiet] [--list] -``` - -### Targets - -| Target | Description | -|--------|-------------| -| `git` | Commit all git repos (push with `--push`) | -| `plakar` | Snapshot to both plakar repos (`s3` and `hoo`) | -| `mobius` | Sync all mobius targets to S3 | -| `<backend>:<name>[:<sub>]` | Run a single target, e.g. `git:nix`, `plakar:s3`, `plakar:hoo`, `plakar:s3:src`, `mobius:www` | - -### Options - -| Flag | Description | -|------|-------------| -| `-p`, `--push` | Push after committing (for git targets) | -| `-q`, `--quiet` | Suppress command output (default: stream output) | -| `-l`, `--list` | List all configured backup targets and exit | -| `-h`, `--help` | Show help | diff --git a/backup/backup.cabal b/backup/backup.cabal @@ -1,18 +0,0 @@ -cabal-version: 3.0 -name: backup -version: 0.1.0 -build-type: Simple - -executable backup - main-is: backup.hs - hs-source-dirs: . - default-language: GHC2024 - default-extensions: OverloadedStrings - ghc-options: -Wall -O2 - - build-depends: - , base >= 4.14 && < 5 - , containers >= 0.6 && < 0.8 - , text >= 2.1 - , time >= 1.9 && < 1.15 - , turtle >= 1.6.2 diff --git a/backup/backup.hs b/backup/backup.hs @@ -1,218 +0,0 @@ -module Main where - -import Control.Monad (forM_) -import Data.List (sort) -import Data.Map.Strict (Map) -import Data.Map.Strict qualified as Map -import Data.Text qualified as T -import Data.Time (defaultTimeLocale, formatTime, getCurrentTime) -import System.Environment (getArgs, withArgs) -import Turtle hiding (sort) - -data Config = Config - { cfgHome :: Text - , cfgGitDirs :: Map Text Text - , cfgPlakarDirs :: Map Text Text - , cfgMobiusDirs :: Map Text Text - } - -mkConfig :: Text -> Config -mkConfig h = - Config - { cfgHome = h - , cfgGitDirs = - Map.fromList - [ ("mpd", h <> "/.config/mpd") - , ("nix", h <> "/src/sr.ht/nix") - , ("nota", h <> "/misc/notes/nota") - , ("pass", h <> "/.password-store") - , ("releases", h <> "/src/haravara.org/releases") - , ("bandcamp", h <> "/misc/music/backlog/_todo") - ] - , cfgPlakarDirs = - Map.fromList - [ ("src", h <> "/src") - , ("misc", h <> "/misc") - , ("local", h <> "/.local") - , ("config", h <> "/.config") - ] - , cfgMobiusDirs = - Map.fromList - [ ("www", h <> "/misc/www") - , ("notes", h <> "/misc/notes") - , ("books", h <> "/misc/books") - , ("docs", h <> "/misc/docs") - , ("mobius", h <> "/misc/mobius") - ] - } - --- Logging - -ts :: IO Text -ts = - T.pack . formatTime defaultTimeLocale "%Y-%m-%dT%H:%M:%S%z" - <$> getCurrentTime - -logMsg :: Text -> IO () -logMsg msg = ts >>= \t -> echo (unsafeTextToLine $ "[" <> t <> "]: " <> msg) - -fatal :: Text -> IO a -fatal msg = do - t <- ts - err (unsafeTextToLine $ "[" <> t <> "]: " <> msg) - exit (ExitFailure 1) - --- Process helpers - -run :: Bool -> Text -> [Text] -> IO ExitCode -run verbose cmd args - | verbose = proc cmd args stdin - | otherwise = fst <$> procStrict cmd args empty - -gitPrefix :: Text -> [Text] -gitPrefix dir = ["--git-dir=" <> dir <> "/.git", "--work-tree=" <> dir] - -git :: Bool -> Text -> [Text] -> IO ExitCode -git verbose dir args = run verbose "git" (gitPrefix dir <> args) - -gitCapture :: Text -> [Text] -> IO (ExitCode, Text) -gitCapture dir args = procStrict "git" (gitPrefix dir <> args) empty - -ok :: ExitCode -> Bool -ok = (== ExitSuccess) - -expect :: Text -> ExitCode -> IO () -expect msg ec = unless (ok ec) $ fatal msg - --- Core logic - -syncRepos :: Text -> Bool -> IO () -syncRepos dir push = do - git False dir ["add", "-A"] - >>= expect ("Failed to stage changes in " <> dir) - - diffEc <- git False dir ["diff", "--cached", "--quiet"] - if ok diffEc - then logMsg $ "Nothing to commit in " <> dir - else do - (changedEc, changed) <- - gitCapture dir ["diff", "--cached", "--name-only"] - expect ("Failed to list staged changes in " <> dir) changedEc - git True dir ["commit", "-m", T.unwords . T.lines $ changed] - >>= expect ("Failed to commit changes in " <> dir) - logMsg $ "Committed changes in " <> dir - - when push $ do - logMsg $ "Pushing changes from " <> dir - git True dir ["push"] >>= expect ("Failed to push changes from " <> dir) - logMsg $ "Done pushing " <> dir - -runPlakar :: Bool -> Text -> [Text] -> Text -> IO ExitCode -runPlakar verbose repo ignores dir = - run verbose "plakar" $ - ["at", repo, "backup"] - <> concatMap (\p -> ["--ignore", p]) ignores - <> [dir] - -s3Ignores :: [Text] -s3Ignores = ["**/.local/share/containers*", "**/Steam*", "**/mnt*", "**/.git*"] - -hooIgnores :: [Text] -hooIgnores = ["**/.cache*", "**/Steam*", "**/mnt*", "**/.git*", "**/.nix*"] - -plakarS3 :: Bool -> Text -> Text -> IO () -plakarS3 verbose name dir = do - logMsg $ "Syncing plakar/s3/" <> name <> " from " <> dir - runPlakar verbose "@estri" s3Ignores dir - >>= expect ("plakar @s3 failed for " <> name) - logMsg $ "Done plakar/s3/" <> name - -plakarHoo :: Bool -> Text -> IO () -plakarHoo verbose dir = do - logMsg $ "Syncing plakar/hoo from " <> dir - runPlakar verbose "@hoo" hooIgnores dir - >>= expect "plakar @hoo failed" - logMsg "Done plakar/hoo" - -mobiusFilters :: Text -> [Text] -mobiusFilters "www" = - ["--exclude", "*", "--include", "*.html", "--include", "*.md"] -mobiusFilters _ = ["--exclude", "*/.git/*"] - -mobiusSync :: Bool -> Text -> Text -> IO () -mobiusSync verbose name dir = do - logMsg $ "Syncing mobius/" <> name <> " from " <> dir - let args = - ["s3", "sync"] - <> mobiusFilters name - <> ["--no-follow-symlinks", dir, "s3://mobius/" <> name] - run verbose "aws" args - >>= expect ("aws s3 sync failed for " <> name) - logMsg $ "Done mobius/" <> name - -listTargets :: Config -> IO () -listTargets cfg = do - let listKeys indent = - mapM_ (echo . unsafeTextToLine . (indent <>)) . sort . Map.keys - echo "git" - listKeys " " (cfgGitDirs cfg) - echo "plakar" - echo " hoo" - echo " s3" - listKeys " " (cfgPlakarDirs cfg) - echo "mobius" - listKeys " " (cfgMobiusDirs cfg) - -backup :: Config -> Text -> Bool -> Bool -> IO () -backup cfg target push verbose = case T.splitOn ":" target of - ["git"] -> - forM_ (Map.toList (cfgGitDirs cfg)) $ \(name, dir) -> - logMsg name >> syncRepos dir push - ["plakar"] -> do - forM_ (Map.toList (cfgPlakarDirs cfg)) $ uncurry (plakarS3 verbose) - plakarHoo verbose (cfgHome cfg) - ["mobius"] -> - forM_ (Map.toList (cfgMobiusDirs cfg)) $ uncurry (mobiusSync verbose) - ["git", name] -> case Map.lookup name (cfgGitDirs cfg) of - Just dir -> syncRepos dir push - Nothing -> fatal $ "Unknown git target: " <> name - ["plakar", "s3"] -> - forM_ (Map.toList (cfgPlakarDirs cfg)) $ uncurry (plakarS3 verbose) - ["plakar", "hoo"] -> plakarHoo verbose (cfgHome cfg) - ["plakar", "s3", name] -> case Map.lookup name (cfgPlakarDirs cfg) of - Just dir -> plakarS3 verbose name dir - Nothing -> fatal $ "Unknown plakar/s3 dir: " <> name - ["mobius", name] -> case Map.lookup name (cfgMobiusDirs cfg) of - Just dir -> mobiusSync verbose name dir - Nothing -> fatal $ "Unknown mobius target: " <> name - _ -> fatal $ "Unknown target: " <> target - -data Args = Args - { argTarget :: Maybe Text - , argPush :: Bool - , argQuiet :: Bool - , argList :: Bool - } - -argsParser :: Parser Args -argsParser = - let targetHelp = "git, plakar, mobius, <backend>:<name>[:<sub>]" - in Args - <$> optional (optText "target" 't' targetHelp) - <*> switch "push" 'p' "Push changes after commit" - <*> switch "quiet" 'q' "Suppress command output" - <*> switch "list" 'l' "List all configured backup targets" - -main :: IO () -main = do - rawArgs <- getArgs - let effArgs = if null rawArgs then ["--help"] else rawArgs - Args mtarget pushFlag quietFlag listFlag <- - withArgs effArgs $ options "" argsParser - h <- need "HOME" >>= maybe (fatal "HOME not set") pure - let cfg = mkConfig h - if listFlag - then listTargets cfg - else case mtarget of - Just target -> backup cfg target pushFlag (not quietFlag) - Nothing -> fatal "no target given (use --list to list targets)"