From 69232cae7a0f970a8fa8a535208d8cb38ad29f18 Mon Sep 17 00:00:00 2001 From: Simon Michael Date: Thu, 17 Apr 2025 01:26:11 -1000 Subject: [PATCH] feat: setup: first setup check: is hledger in PATH ? --- hledger/Hledger/Cli.hs | 2 +- hledger/Hledger/Cli/Commands.hs | 2 +- hledger/Hledger/Cli/Commands/Setup.hs | 168 ++++++++++++++++++++------ hledger/Hledger/Cli/Commands/Setup.md | 2 +- hledger/hledger.m4.md | 2 +- 5 files changed, 138 insertions(+), 38 deletions(-) diff --git a/hledger/Hledger/Cli.hs b/hledger/Hledger/Cli.hs index aec30c9bc..bb1f07ec0 100644 --- a/hledger/Hledger/Cli.hs +++ b/hledger/Hledger/Cli.hs @@ -421,7 +421,7 @@ main = exitOnExceptions $ withGhcDebug' $ do | manFlag -> runManForTopic "hledger" mmodecmdname -- 6.5.2. builtin command which should not require or read the journal - run it - | cmdname `elem` ["commands","demo","help","test"] -> + | cmdname `elem` ["commands","demo","help","setup","test"] -> cmdaction opts (ignoredjournal cmdname) -- 6.5.3. builtin command which should create the journal if missing - do that and run it diff --git a/hledger/Hledger/Cli/Commands.hs b/hledger/Hledger/Cli/Commands.hs index 8cb6ea8e7..4da675294 100644 --- a/hledger/Hledger/Cli/Commands.hs +++ b/hledger/Hledger/Cli/Commands.hs @@ -295,7 +295,7 @@ commandsList progversion othercmds = ," diff compare an account's transactions in two journals" ,"+git save or view journal file history simply in git" -- hledger-git ,"+pijul save or view journal file history simply in pijul" -- hledger-pijul - ," setup check and help set up various installation things" + ," setup check and show the status of the hledger installation" ," test run some self tests" ,"" -----------------------------------------80------------------------------------- diff --git a/hledger/Hledger/Cli/Commands/Setup.hs b/hledger/Hledger/Cli/Commands/Setup.hs index 67b5baf1a..4e3b89ca7 100644 --- a/hledger/Hledger/Cli/Commands/Setup.hs +++ b/hledger/Hledger/Cli/Commands/Setup.hs @@ -1,6 +1,8 @@ {-| -Check and help set up various installation things. +Check and show the status of the hledger installation, +show extra info and hints, +and offer to fix problems where possible. -} @@ -15,27 +17,18 @@ module Hledger.Cli.Commands.Setup ( ) where --- import Data.Default (def) --- import System.FilePath (takeFileName) --- import Data.List (intercalate, nub, sortOn) --- import Data.List.Extra (nubSort) --- import qualified Data.Map as Map --- import Data.Maybe (fromMaybe) --- import Data.HashSet (size, fromList) --- import qualified Data.Text as T --- import qualified Data.Text.Lazy as TL --- import qualified Data.Text.Lazy.Builder as TB --- import Data.Time.Calendar (Day, addDays, diffDays) --- import Data.Time.Clock.POSIX (getPOSIXTime) --- import GHC.Stats --- -- import System.Console.CmdArgs.Explicit hiding (Group) --- import System.Mem (performMajorGC) --- import Text.Printf (printf) --- import Text.Tabular.AsciiWide - +import System.FilePath +import Data.Text (Text) +import qualified Data.Text as T +import qualified Data.Text.IO as T import Hledger import Hledger.Cli.CliOptions --- import Hledger.Cli.Utils (writeOutputLazyText) +import Control.Monad +import System.Info +import System.Directory +import System.IO +import Safe +-- import Text.Printf (printf) setupmode = hledgerCommandMode @@ -45,20 +38,127 @@ setupmode = hledgerCommandMode hiddenflags ([], Just $ argsFlag "[QUERY]") --- like Register.summarisePostings --- | Print various statistics for the journal. +-- | 1. Check and show the status of various aspects of the hledger installation. +-- 2. Show extra info and hints on how to fix problems. +-- 3. When possible, offer to help fix problems, interactively. setup :: CliOpts -> Journal -> IO () -setup _opts@CliOpts{rawopts_=_rawopts, reportspec_=_rspec} _j = do - print "setup" +setup _opts@CliOpts{rawopts_=_rawopts, reportspec_=_rspec} _ignoredj = do + -- This command is not given a journal and should not use _ignoredj; + -- instead detect it ourselves when we are ready. + let + p ok ymsg nmsg = + putStrLn $ unwords $ if ok then ["", y, "", ymsg] else ["", n, "", nmsg] + where + y = "yes ✅" + n = "no ❌" + putStrLn "hledger:" - -- let today = _rsDay rspec - -- verbose = boolopt "verbose" rawopts - -- q = _rsQuery rspec - -- l = ledgerFromJournal q j - -- intervalspans = snd $ reportSpanBothDates j rspec - -- ismultiperiod = length intervalspans > 1 - -- (ls, txncounts) = unzip $ map (showLedgerStats verbose l today) intervalspans - -- numtxns = sum txncounts - -- txt = (if ismultiperiod then id else TL.init) $ TB.toLazyText $ unlinesB ls - -- writeOutputLazyText opts txt + putStr "- in PATH ?" + pathexes <- findExecutables progname + home <- getHomeDirectory + appdata <- getXdgDirectory XdgData "" + otherexes <- flip findExecutablesInDirectories progname $ + [home ".local/bin" + ,home ".cabal/bin" + ,home ".nix-profile/bin" + ,"/opt/homebrew/bin" + ,"/usr/local/bin" + ,"/usr/bin" + ] + ++ [appdata "local/bin" | os == "mingw32"] + ++ [appdata "cabal/bin" | os == "mingw32"] + let + ok = not $ null pathexes + pathexe = quoteIfNeeded $ headDef (error' "showing nonexistent executable") pathexes + otherexe = quoteIfNeeded $ headDef (error' "showing nonexistent executable") otherexes + otherdir = takeDirectory otherexe + hint = if null otherexes + then ("Add " <> progname <> "'s directory to your shell's PATH.") + else unlines + ["Add " <> otherdir <> " to PATH in your shell config." + ," Eg on unix: echo 'export PATH=" <> otherdir <> ":$PATH' >> ~/.profile" + ," and start a new shell session." + ] + p ok pathexe hint + + -- putStr "- runnable ?" + + -- putStr "- up to date ?" + + -- putStr "- native binary ?" + + -- putStr "- eget installed ?" + + putStr "\n" + + -- putStrLn "config:" + -- putStr "- user config file exists ?" + -- putStr "\n" + -- putStr "- local config masking user config ?" + -- putStr "\n" + -- putStr "- config file readable ?" + -- putStr "\n" + -- putStr "- common general options configured ?" + -- putStr "\n" + -- putStr " --pretty --ignore-assertions --infer-costs" + -- putStr "\n" + -- putStr " print --explicit --show-costs" + -- putStr "\n" + -- putStr "\n" + + -- putStrLn "files:" + -- putStr "- default journal file exists ?" + -- putStr "\n" + -- putStr "- default journal file readable ?" + -- putStr "\n" + -- putStr "\n" + + -- putStrLn "accounts:" + -- putStr "- all account types declared or detected ?" + -- putStr "\n" + -- putStr " asset, liability, equity, revenue, expense, cash, conversion" + -- putStr "\n" + -- putStr "- untyped accounts ?" + -- putStr "\n" + -- putStr "- all used accounts declared ?" + -- putStr "\n" + -- putStr "\n" + + -- putStrLn "commodities:" + -- putStr "- all used commodities declared ?" + -- putStr "\n" + -- putStr "\n" + + -- putStrLn "tags:" + -- putStr "- all used tags declared ?" + -- putStr "\n" + -- putStr "\n" + +{- | Ensure there is a journal file at the given path, creating an empty one if needed. +On Windows, also ensure that the path contains no trailing dots +which could cause data loss (see 'isWindowsUnsafeDotPath'). +-} +_ensureJournalFileExists :: FilePath -> IO () +_ensureJournalFileExists f = do + when (os == "mingw32" && isWindowsUnsafeDotPath f) $ + error' $ + "Part of file path \"" <> show f <> "\"\n ends with a dot, which is unsafe on Windows; please use a different path.\n" + exists <- doesFileExist f + unless exists $ do + hPutStrLn stderr $ "Creating hledger journal file " <> show f + -- note Hledger.Utils.UTF8.* do no line ending conversion on windows, + -- we currently require unix line endings on all platforms. + newJournalContent >>= T.writeFile f + +{- | Does any part of this path contain non-. characters and end with a . ? +Such paths are not safe to use on Windows (cf #1056). +-} +isWindowsUnsafeDotPath :: FilePath -> Bool +isWindowsUnsafeDotPath = any (\x -> last x == '.' && any (/= '.') x) . splitDirectories + +-- | Give the content for a new auto-created journal file. +newJournalContent :: IO Text +newJournalContent = do + d <- getCurrentDay + return $ "; journal created " <> T.pack (show d) <> " by hledger\n" diff --git a/hledger/Hledger/Cli/Commands/Setup.md b/hledger/Hledger/Cli/Commands/Setup.md index 5a251b715..9f61e917e 100644 --- a/hledger/Hledger/Cli/Commands/Setup.md +++ b/hledger/Hledger/Cli/Commands/Setup.md @@ -1,6 +1,6 @@ ## setup -Check and help set up various installation things. +Check and show the status of the hledger installation. ```flags Flags: diff --git a/hledger/hledger.m4.md b/hledger/hledger.m4.md index c364113a1..65956a380 100644 --- a/hledger/hledger.m4.md +++ b/hledger/hledger.m4.md @@ -6515,7 +6515,7 @@ If you have installed more [add-on commands](../scripts.md), they also will be l - [check](#check) - check for various kinds of error in the data - [diff](#diff) - compare account transactions in two journal files -- [setup](#setup) - check and help set up various installation things +- [setup](#setup) - check and show the status of the hledger installation - [test](#test) - run self tests