From 93d5d8ddd812ff63b069b47c9d8eafa803eeb3a2 Mon Sep 17 00:00:00 2001 From: q9i <46249765+quantum9Innovation@users.noreply.github.com> Date: Mon, 22 Jul 2024 14:06:18 -0700 Subject: [PATCH 01/20] refactor: create types and const modules --- app/Const.hs | 13 +++++++++++++ app/Main.hs | 44 +++----------------------------------------- app/Types.hs | 32 ++++++++++++++++++++++++++++++++ suntheme.cabal | 20 ++++++++++---------- 4 files changed, 58 insertions(+), 51 deletions(-) create mode 100644 app/Const.hs create mode 100644 app/Types.hs diff --git a/app/Const.hs b/app/Const.hs new file mode 100644 index 0000000..607aa9c --- /dev/null +++ b/app/Const.hs @@ -0,0 +1,13 @@ +module Const where + +query :: String +query = "http://ip-api.com/line/?fields=status,lat,lon,timezone" + +prog :: String +prog = "suntheme" + +lightModeScript :: String +lightModeScript = "light.sh" + +darkModeScript :: String +darkModeScript = "dark.sh" diff --git a/app/Main.hs b/app/Main.hs index 94f69de..aba8926 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -5,6 +5,9 @@ module Main where +import Const (darkModeScript, lightModeScript, prog, query) +import Types (ResponseMsg(..), ResponseCode(..), Status(..), genericErr, toResponseMsg) + import Data.Time (ZonedTime, getCurrentTime) import Data.Time.Solar (Location(Location), sunrise, sunset) import Data.Time.RFC3339 (formatTimeRFC3339) @@ -25,47 +28,6 @@ import Network.HTTP.Request (Response(responseBody, responseStatus), get) -- introduce liquid types and checking -- whitepaper! -class Status s where - ok :: s -> Bool - ok = const False - - disp :: s -> String - disp = const "encountered bad response (expected OK)" - -newtype ResponseCode = ResponseCode Int -instance Status ResponseCode where - ok (ResponseCode code) = code == 200 - disp (ResponseCode code) = - "encountered bad response code: " - ++ show code - ++ " (expected 200 'OK')" - -newtype ResponseMsg = ResponseMsg String -instance Status ResponseMsg where - ok (ResponseMsg msg) = msg == "success" - disp (ResponseMsg msg) = - "encountered bad response message: " - ++ msg - ++ " (expected 'success')" - -toResponseMsg :: SomeException -> ResponseMsg -toResponseMsg = ResponseMsg . show - -query :: String -query = "http://ip-api.com/line/?fields=status,lat,lon,timezone" - -genericErr :: ResponseMsg -genericErr = ResponseMsg "encountered unknown error" - -prog :: String -prog = "suntheme" - -lightModeScript :: String -lightModeScript = "light.sh" - -darkModeScript :: String -darkModeScript = "dark.sh" - now :: IO ZonedTime now = do utcTime <- getCurrentTime diff --git a/app/Types.hs b/app/Types.hs new file mode 100644 index 0000000..5601caa --- /dev/null +++ b/app/Types.hs @@ -0,0 +1,32 @@ +module Types where + +import Control.Exception (SomeException) + +class Status s where + ok :: s -> Bool + ok = const False + + disp :: s -> String + disp = const "encountered bad response (expected OK)" + +newtype ResponseCode = ResponseCode Int +instance Status ResponseCode where + ok (ResponseCode code) = code == 200 + disp (ResponseCode code) = + "encountered bad response code: " + ++ show code + ++ " (expected 200 'OK')" + +newtype ResponseMsg = ResponseMsg String +instance Status ResponseMsg where + ok (ResponseMsg msg) = msg == "success" + disp (ResponseMsg msg) = + "encountered bad response message: " + ++ msg + ++ " (expected 'success')" + +toResponseMsg :: SomeException -> ResponseMsg +toResponseMsg = ResponseMsg . show + +genericErr :: ResponseMsg +genericErr = ResponseMsg "encountered unknown error" diff --git a/suntheme.cabal b/suntheme.cabal index fef2dfb..801f81d 100644 --- a/suntheme.cabal +++ b/suntheme.cabal @@ -62,22 +62,22 @@ executable suntheme main-is: Main.hs -- Modules included in this executable, other than Main. - -- other-modules: + other-modules: Types, Const -- LANGUAGE extensions used by modules in this package. -- other-extensions: -- Other library packages from which modules are imported. build-depends: base ^>=4.17.2.1, - request ^>=0.2.2.0, - process ^>=1.6.18.0, - bytestring ^>=0.11.5.3, - filepath ^>=1.4.2.2, - time ^>=1.9.3, - solar ^>=0.1.0.0, - timerep ^>=2.1.0.0, - extra ^>=1.7.16, - directory ^>=1.3.8.5 + request ^>=0.2.2.0, + process ^>=1.6.18.0, + bytestring ^>=0.11.5.3, + filepath ^>=1.4.2.2, + time ^>=1.9.3, + solar ^>=0.1.0.0, + timerep ^>=2.1.0.0, + extra ^>=1.7.16, + directory ^>=1.3.8.5 -- Directories containing source files. hs-source-dirs: app From 9258461e3ce99b968558899eb3f2d8d1da43e964 Mon Sep 17 00:00:00 2001 From: q9i <46249765+quantum9Innovation@users.noreply.github.com> Date: Mon, 22 Jul 2024 14:21:55 -0700 Subject: [PATCH 02/20] refactor: create pure module `Pure` contains functions for processing data and building commands. The module includes functions for reading lines of input, formatting time, and constructing a command with a given script and time. --- app/Main.hs | 18 +----------------- app/Pure.hs | 20 ++++++++++++++++++++ suntheme.cabal | 2 +- 3 files changed, 22 insertions(+), 18 deletions(-) create mode 100644 app/Pure.hs diff --git a/app/Main.hs b/app/Main.hs index aba8926..9d51013 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -5,12 +5,12 @@ module Main where +import Pure (buildCmd, kill, readLines) import Const (darkModeScript, lightModeScript, prog, query) import Types (ResponseMsg(..), ResponseCode(..), Status(..), genericErr, toResponseMsg) import Data.Time (ZonedTime, getCurrentTime) import Data.Time.Solar (Location(Location), sunrise, sunset) -import Data.Time.RFC3339 (formatTimeRFC3339) import Data.Time.LocalTime (getTimeZone, utcToZonedTime, zonedTimeToUTC) import Data.List.Extra ((!?)) import Data.ByteString.Char8 (unpack) @@ -77,10 +77,6 @@ ping run err = do runner = run r in destruct status runner err -readLines :: [String] -> Maybe (String, Double, Double, String) -readLines [msg, latStr, lonStr, tz] = Just (msg, read latStr, read lonStr, tz) -readLines _ = Nothing - process :: Response -> (Double -> Double -> String -> IO ()) -> IO () -> IO () process r run err = case info of @@ -130,9 +126,6 @@ backupRunner = do exec :: String -> (SomeException -> IO String) -> IO String exec cmd err = do catch (readProcess "bash" ["-c", cmd] "") err -kill :: String -> String -kill = (++) "atrm " - killall :: [String] -> IO () killall = foldr ((>>) . dispatch . kill) (return ()) where @@ -160,15 +153,6 @@ finish queue = do getId = head . words . last . lines failure = print :: SomeException -> IO () -after :: (Eq a) => a -> [a] -> [a] -after c = drop 1 . dropWhile (/= c) - -formatTime :: ZonedTime -> String -formatTime = take 5 . after 'T' . formatTimeRFC3339 - -buildCmd :: String -> ZonedTime -> String -buildCmd script time = "echo \"" ++ script ++ "\" | at " ++ formatTime time - activateOnSunrise :: ZonedTime -> ZonedTime -> IO Bool activateOnSunrise sunriseTime sunsetTime = do timeNow <- now diff --git a/app/Pure.hs b/app/Pure.hs new file mode 100644 index 0000000..30ceef1 --- /dev/null +++ b/app/Pure.hs @@ -0,0 +1,20 @@ +module Pure where + +import Data.Time (ZonedTime) +import Data.Time.RFC3339 (formatTimeRFC3339) + +readLines :: [String] -> Maybe (String, Double, Double, String) +readLines [msg, latStr, lonStr, tz] = Just (msg, read latStr, read lonStr, tz) +readLines _ = Nothing + +kill :: String -> String +kill = (++) "atrm " + +after :: (Eq a) => a -> [a] -> [a] +after c = drop 1 . dropWhile (/= c) + +formatTime :: ZonedTime -> String +formatTime = take 5 . after 'T' . formatTimeRFC3339 + +buildCmd :: String -> ZonedTime -> String +buildCmd script time = "echo \"" ++ script ++ "\" | at " ++ formatTime time diff --git a/suntheme.cabal b/suntheme.cabal index 801f81d..d2c530e 100644 --- a/suntheme.cabal +++ b/suntheme.cabal @@ -62,7 +62,7 @@ executable suntheme main-is: Main.hs -- Modules included in this executable, other than Main. - other-modules: Types, Const + other-modules: Types, Const, Pure -- LANGUAGE extensions used by modules in this package. -- other-extensions: From 4f0db471bcb1f774c7b41d9892a9fff962a2b739 Mon Sep 17 00:00:00 2001 From: q9i <46249765+quantum9Innovation@users.noreply.github.com> Date: Mon, 22 Jul 2024 14:47:11 -0700 Subject: [PATCH 03/20] refactor: create time module move time-related functions from `Main.hs` to a new `Time` module for better organization and code separation --- app/Main.hs | 31 +------------------------------ app/Pure.hs | 8 ++++++++ app/Time.hs | 28 ++++++++++++++++++++++++++++ suntheme.cabal | 2 +- 4 files changed, 38 insertions(+), 31 deletions(-) create mode 100644 app/Time.hs diff --git a/app/Main.hs b/app/Main.hs index 9d51013..2be7bac 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -6,12 +6,10 @@ module Main where import Pure (buildCmd, kill, readLines) +import Time (activateOnSunrise, sunriseNow, sunsetNow) import Const (darkModeScript, lightModeScript, prog, query) import Types (ResponseMsg(..), ResponseCode(..), Status(..), genericErr, toResponseMsg) -import Data.Time (ZonedTime, getCurrentTime) -import Data.Time.Solar (Location(Location), sunrise, sunset) -import Data.Time.LocalTime (getTimeZone, utcToZonedTime, zonedTimeToUTC) import Data.List.Extra ((!?)) import Data.ByteString.Char8 (unpack) import System.Exit (ExitCode(ExitFailure), exitWith) @@ -28,24 +26,6 @@ import Network.HTTP.Request (Response(responseBody, responseStatus), get) -- introduce liquid types and checking -- whitepaper! -now :: IO ZonedTime -now = do - utcTime <- getCurrentTime - timeZone <- getTimeZone utcTime - return (utcToZonedTime timeZone utcTime) - -sunriseNow :: Double -> Double -> IO ZonedTime -sunriseNow lat lon = do - time <- now - return (sunrise time here) - where here = Location lat lon - -sunsetNow :: Double -> Double -> IO ZonedTime -sunsetNow lat lon = do - time <- now - return (sunset time here) - where here = Location lat lon - pathToCache :: String -> IO String pathToCache str = do dir <- getXdgDirectory XdgCache prog @@ -153,15 +133,6 @@ finish queue = do getId = head . words . last . lines failure = print :: SomeException -> IO () -activateOnSunrise :: ZonedTime -> ZonedTime -> IO Bool -activateOnSunrise sunriseTime sunsetTime = do - timeNow <- now - let utcTimeNow = zonedTimeToUTC timeNow - utcSunrise = zonedTimeToUTC sunriseTime - utcSunset = zonedTimeToUTC sunsetTime - if utcTimeNow < utcSunrise || utcTimeNow > utcSunset then return True - else return False - prepareScripts :: Double -> Double -> IO () prepareScripts lat lon = do sunriseTime <- sunriseNow lat lon diff --git a/app/Pure.hs b/app/Pure.hs index 30ceef1..fba5ece 100644 --- a/app/Pure.hs +++ b/app/Pure.hs @@ -2,6 +2,7 @@ module Pure where import Data.Time (ZonedTime) import Data.Time.RFC3339 (formatTimeRFC3339) +import Data.Time.LocalTime (zonedTimeToUTC) readLines :: [String] -> Maybe (String, Double, Double, String) readLines [msg, latStr, lonStr, tz] = Just (msg, read latStr, read lonStr, tz) @@ -18,3 +19,10 @@ formatTime = take 5 . after 'T' . formatTimeRFC3339 buildCmd :: String -> ZonedTime -> String buildCmd script time = "echo \"" ++ script ++ "\" | at " ++ formatTime time + +sunriseNext :: ZonedTime -> ZonedTime -> ZonedTime -> Bool +sunriseNext sunriseTime sunsetTime time = + let utcTimeNow = zonedTimeToUTC time + utcSunrise = zonedTimeToUTC sunriseTime + utcSunset = zonedTimeToUTC sunsetTime + in utcTimeNow < utcSunrise || utcTimeNow > utcSunset diff --git a/app/Time.hs b/app/Time.hs new file mode 100644 index 0000000..2594007 --- /dev/null +++ b/app/Time.hs @@ -0,0 +1,28 @@ +module Time where + +import Pure (sunriseNext) + +import Data.Time (ZonedTime, getCurrentTime) +import Data.Time.Solar (Location(Location), sunrise, sunset) +import Data.Time.LocalTime (getTimeZone, utcToZonedTime) + +now :: IO ZonedTime +now = do + utcTime <- getCurrentTime + timeZone <- getTimeZone utcTime + return (utcToZonedTime timeZone utcTime) + +sunriseNow :: Double -> Double -> IO ZonedTime +sunriseNow lat lon = do + time <- now + return (sunrise time here) + where here = Location lat lon + +sunsetNow :: Double -> Double -> IO ZonedTime +sunsetNow lat lon = do + time <- now + return (sunset time here) + where here = Location lat lon + +activateOnSunrise :: ZonedTime -> ZonedTime -> IO Bool +activateOnSunrise sunriseTime sunsetTime = sunriseNext sunriseTime sunsetTime <$> now diff --git a/suntheme.cabal b/suntheme.cabal index d2c530e..2f3e41c 100644 --- a/suntheme.cabal +++ b/suntheme.cabal @@ -62,7 +62,7 @@ executable suntheme main-is: Main.hs -- Modules included in this executable, other than Main. - other-modules: Types, Const, Pure + other-modules: Types, Const, Pure, Time -- LANGUAGE extensions used by modules in this package. -- other-extensions: From 9fbc5d0f534c2419946df373d556107080964ade Mon Sep 17 00:00:00 2001 From: q9i <46249765+quantum9Innovation@users.noreply.github.com> Date: Mon, 22 Jul 2024 15:27:06 -0700 Subject: [PATCH 04/20] refactor: create sugar module move IO abstractions and desugaring processes into a separate module --- app/Main.hs | 34 ++++++---------------------------- app/Sugar.hs | 28 ++++++++++++++++++++++++++++ suntheme.cabal | 2 +- 3 files changed, 35 insertions(+), 29 deletions(-) create mode 100644 app/Sugar.hs diff --git a/app/Main.hs b/app/Main.hs index 2be7bac..3c43d4c 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -5,15 +5,14 @@ module Main where -import Pure (buildCmd, kill, readLines) +import Pure (buildCmd, readLines) import Time (activateOnSunrise, sunriseNow, sunsetNow) import Const (darkModeScript, lightModeScript, prog, query) -import Types (ResponseMsg(..), ResponseCode(..), Status(..), genericErr, toResponseMsg) +import Sugar (continue, crash, destruct, exec, killall) +import Types (ResponseMsg(..), ResponseCode(..), genericErr, toResponseMsg) import Data.List.Extra ((!?)) import Data.ByteString.Char8 (unpack) -import System.Exit (ExitCode(ExitFailure), exitWith) -import System.Process (readProcess) import System.FilePath ((), takeDirectory) import System.Directory (XdgDirectory(XdgCache, XdgConfig), createDirectoryIfMissing, doesFileExist, getXdgDirectory) import Control.Exception (SomeException, catch, try) @@ -39,19 +38,11 @@ pathToConfig str = do fetch :: String -> IO (Either SomeException Response) fetch = try . get -cont :: (Status s) => s -> IO () -> IO () -cont e err = (putStrLn . disp) e >> err - -destruct :: (Status s) => s -> IO () -> IO () -> IO () -destruct status success failure - | ok status = success - | otherwise = cont status failure - ping :: (Response -> IO ()) -> IO () -> IO () ping run err = do res <- fetch query case res of - Left e -> cont (toResponseMsg e) err + Left e -> continue (toResponseMsg e) err Right r -> let status = (ResponseCode . responseStatus) r runner = run r @@ -60,7 +51,7 @@ ping run err = do process :: Response -> (Double -> Double -> String -> IO ()) -> IO () -> IO () process r run err = case info of - Nothing -> cont genericErr err + Nothing -> continue genericErr err Just (msg, lat, lon, tz) -> let status = ResponseMsg msg runner = run lat lon tz @@ -91,9 +82,6 @@ readCache = do processRunner :: Response -> IO () processRunner r = process r dumpCache backupRunner -crash :: IO () -crash = exitWith (ExitFailure 1) - backupRunner :: IO () backupRunner = do contents <- readCache @@ -103,16 +91,6 @@ backupRunner = do crash Just (lat, lon, _) -> prepareScripts lat lon -exec :: String -> (SomeException -> IO String) -> IO String -exec cmd err = do catch (readProcess "bash" ["-c", cmd] "") err - -killall :: [String] -> IO () -killall = foldr ((>>) . dispatch . kill) (return ()) - where - dispatch cmd = exec cmd failure - failure :: SomeException -> IO String - failure e = print e >> return [] - start :: IO () -> IO () start act = do logFile <- pathToCache "log.txt" @@ -120,7 +98,7 @@ start act = do if existsLog then do contents <- readFile logFile let entries = lines contents - killall entries + (sequence_ . killall) entries else createDirectoryIfMissing True (takeDirectory logFile) act diff --git a/app/Sugar.hs b/app/Sugar.hs new file mode 100644 index 0000000..4c6658e --- /dev/null +++ b/app/Sugar.hs @@ -0,0 +1,28 @@ +module Sugar where + +import Pure (kill) +import Types (Status(..)) + +import System.Exit (ExitCode(ExitFailure), exitWith) +import System.Process (readProcess) +import Control.Exception (SomeException, catch) + +throw :: (Monoid m) => SomeException -> IO m +throw e = print e >> return mempty + +continue :: (Status s) => s -> IO () -> IO () +continue e err = (putStrLn . disp) e >> err + +destruct :: (Status s) => s -> IO () -> IO () -> IO () +destruct status success failure + | ok status = success + | otherwise = continue status failure + +crash :: IO () +crash = (exitWith . ExitFailure) 1 + +exec :: String -> (SomeException -> IO String) -> IO String +exec cmd = catch (readProcess "bash" ["-c", cmd] "") + +killall :: [String] -> [IO String] +killall = map (dispatch . kill) where dispatch cmd = exec cmd throw diff --git a/suntheme.cabal b/suntheme.cabal index 2f3e41c..0c421aa 100644 --- a/suntheme.cabal +++ b/suntheme.cabal @@ -62,7 +62,7 @@ executable suntheme main-is: Main.hs -- Modules included in this executable, other than Main. - other-modules: Types, Const, Pure, Time + other-modules: Types, Const, Pure, Time, Sugar -- LANGUAGE extensions used by modules in this package. -- other-extensions: From 76973ba364ea49481c95c09a1ed400f9444ba4df Mon Sep 17 00:00:00 2001 From: q9i <46249765+quantum9Innovation@users.noreply.github.com> Date: Mon, 22 Jul 2024 15:39:35 -0700 Subject: [PATCH 05/20] refactor: create getters module group helper funcs for creating directory names and fetching network resources into a single module --- app/Getters.hs | 17 +++++++++++++++++ app/Main.hs | 26 +++++++------------------- suntheme.cabal | 2 +- 3 files changed, 25 insertions(+), 20 deletions(-) create mode 100644 app/Getters.hs diff --git a/app/Getters.hs b/app/Getters.hs new file mode 100644 index 0000000..96087d9 --- /dev/null +++ b/app/Getters.hs @@ -0,0 +1,17 @@ +module Getters where + +import Const (prog) + +import System.FilePath (()) +import System.Directory (XdgDirectory(XdgCache, XdgConfig), getXdgDirectory) +import Control.Exception (SomeException, try) +import Network.HTTP.Request (Response, get) + +pathToCache :: String -> IO String +pathToCache str = ( str) <$> getXdgDirectory XdgCache prog + +pathToConfig :: String -> IO String +pathToConfig str = ( str) <$> getXdgDirectory XdgConfig prog + +fetch :: String -> IO (Either SomeException Response) +fetch = try . get diff --git a/app/Main.hs b/app/Main.hs index 3c43d4c..1a1f823 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -7,16 +7,17 @@ module Main where import Pure (buildCmd, readLines) import Time (activateOnSunrise, sunriseNow, sunsetNow) -import Const (darkModeScript, lightModeScript, prog, query) +import Const (darkModeScript, lightModeScript, query) import Sugar (continue, crash, destruct, exec, killall) -import Types (ResponseMsg(..), ResponseCode(..), genericErr, toResponseMsg) +import Types (ResponseCode(..), ResponseMsg(..), genericErr, toResponseMsg) +import Getters (fetch, pathToCache, pathToConfig) import Data.List.Extra ((!?)) import Data.ByteString.Char8 (unpack) -import System.FilePath ((), takeDirectory) -import System.Directory (XdgDirectory(XdgCache, XdgConfig), createDirectoryIfMissing, doesFileExist, getXdgDirectory) -import Control.Exception (SomeException, catch, try) -import Network.HTTP.Request (Response(responseBody, responseStatus), get) +import System.FilePath (takeDirectory) +import System.Directory (createDirectoryIfMissing, doesFileExist) +import Control.Exception (SomeException, catch) +import Network.HTTP.Request (Response(responseBody, responseStatus)) -- error handling -- refactor into modules @@ -25,19 +26,6 @@ import Network.HTTP.Request (Response(responseBody, responseStatus), get) -- introduce liquid types and checking -- whitepaper! -pathToCache :: String -> IO String -pathToCache str = do - dir <- getXdgDirectory XdgCache prog - return (dir str) - -pathToConfig :: String -> IO String -pathToConfig str = do - dir <- getXdgDirectory XdgConfig prog - return (dir str) - -fetch :: String -> IO (Either SomeException Response) -fetch = try . get - ping :: (Response -> IO ()) -> IO () -> IO () ping run err = do res <- fetch query diff --git a/suntheme.cabal b/suntheme.cabal index 0c421aa..9499d09 100644 --- a/suntheme.cabal +++ b/suntheme.cabal @@ -62,7 +62,7 @@ executable suntheme main-is: Main.hs -- Modules included in this executable, other than Main. - other-modules: Types, Const, Pure, Time, Sugar + other-modules: Types, Const, Pure, Time, Sugar, Getters -- LANGUAGE extensions used by modules in this package. -- other-extensions: From f22d715b8e497459766a3cc6bb1ce91fa86385f8 Mon Sep 17 00:00:00 2001 From: q9i <46249765+quantum9Innovation@users.noreply.github.com> Date: Thu, 1 Aug 2024 20:15:32 -0700 Subject: [PATCH 06/20] refactor: complete modularization introduce Workers, Cache modules and shuffle around some existing functions --- app/Cache.hs | 51 ++++++++++++++++++++++ app/Const.hs | 6 +++ app/Getters.hs | 10 ----- app/Main.hs | 116 +++++++++++-------------------------------------- app/Sugar.hs | 7 ++- app/Time.hs | 18 +++++++- app/Workers.hs | 26 +++++++++++ suntheme.cabal | 2 +- 8 files changed, 131 insertions(+), 105 deletions(-) create mode 100644 app/Cache.hs create mode 100644 app/Workers.hs diff --git a/app/Cache.hs b/app/Cache.hs new file mode 100644 index 0000000..ef0ebef --- /dev/null +++ b/app/Cache.hs @@ -0,0 +1,51 @@ +module Cache where + +import Const (cacheFile, logFile, prog) +import Sugar (killall, failure) + +import Data.List.Extra ((!?)) +import System.FilePath ((), takeDirectory) +import System.Directory ( + XdgDirectory(XdgCache, XdgConfig), + createDirectoryIfMissing, doesFileExist, getXdgDirectory + ) +import Control.Exception (catch) + +pathToCache :: String -> IO String +pathToCache str = ( str) <$> getXdgDirectory XdgCache prog + +pathToConfig :: String -> IO String +pathToConfig str = ( str) <$> getXdgDirectory XdgConfig prog + +readCache :: IO (Maybe (Double, Double, String)) +readCache = do + cache <- pathToCache cacheFile + contents <- readFile cache + let entries = lines contents + case (entries !? 0, entries !? 1, entries !? 2) of + (Just lat, Just lon, Just tz) -> return (Just (read lat, read lon, tz)) + _ -> return Nothing + +dumpCache :: Double -> Double -> String -> IO (Double, Double) +dumpCache lat lon tz = do + cache <- pathToCache cacheFile + catch (writer cache) failure + return (lat, lon) + where writer dir = writeFile dir (show lat ++ "\n" ++ show lon ++ "\n" ++ tz) + +start :: IO () +start = do + logs <- pathToCache logFile + existsLog <- doesFileExist logs + if existsLog then do + contents <- readFile logs + (sequence_ . killall . lines) contents + else createDirectoryIfMissing True (takeDirectory logs) + +finish :: String -> IO () +finish queue = do + cache <- pathToCache logFile + catch (writeFile cache num) failure + where + getId = head . words . last . lines + num = getId queue diff --git a/app/Const.hs b/app/Const.hs index 607aa9c..3af5223 100644 --- a/app/Const.hs +++ b/app/Const.hs @@ -6,6 +6,12 @@ query = "http://ip-api.com/line/?fields=status,lat,lon,timezone" prog :: String prog = "suntheme" +cacheFile :: String +cacheFile = "data.txt" + +logFile :: String +logFile = "log.txt" + lightModeScript :: String lightModeScript = "light.sh" diff --git a/app/Getters.hs b/app/Getters.hs index 96087d9..6239d4d 100644 --- a/app/Getters.hs +++ b/app/Getters.hs @@ -1,17 +1,7 @@ module Getters where -import Const (prog) - -import System.FilePath (()) -import System.Directory (XdgDirectory(XdgCache, XdgConfig), getXdgDirectory) import Control.Exception (SomeException, try) import Network.HTTP.Request (Response, get) -pathToCache :: String -> IO String -pathToCache str = ( str) <$> getXdgDirectory XdgCache prog - -pathToConfig :: String -> IO String -pathToConfig str = ( str) <$> getXdgDirectory XdgConfig prog - fetch :: String -> IO (Either SomeException Response) fetch = try . get diff --git a/app/Main.hs b/app/Main.hs index 1a1f823..9d4ac6b 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -5,37 +5,29 @@ module Main where -import Pure (buildCmd, readLines) -import Time (activateOnSunrise, sunriseNow, sunsetNow) -import Const (darkModeScript, lightModeScript, query) -import Sugar (continue, crash, destruct, exec, killall) +import Pure (readLines) +import Cache (dumpCache, start) +import Const (query) +import Sugar (continue, destruct) import Types (ResponseCode(..), ResponseMsg(..), genericErr, toResponseMsg) -import Getters (fetch, pathToCache, pathToConfig) +import Getters (fetch) +import Workers (backupRunner, prepareScripts) -import Data.List.Extra ((!?)) import Data.ByteString.Char8 (unpack) -import System.FilePath (takeDirectory) -import System.Directory (createDirectoryIfMissing, doesFileExist) -import Control.Exception (SomeException, catch) import Network.HTTP.Request (Response(responseBody, responseStatus)) -- error handling --- refactor into modules --- clean up jank (reduce lines in functions, more atomic/functional, etc) +-- clean up jank ( +-- create types, +-- more pure functions, +-- reduce lines in functions, +-- more atomic/functional, +-- etc +-- ) -- eliminate as many do blocks as possible -- introduce liquid types and checking -- whitepaper! -ping :: (Response -> IO ()) -> IO () -> IO () -ping run err = do - res <- fetch query - case res of - Left e -> continue (toResponseMsg e) err - Right r -> - let status = (ResponseCode . responseStatus) r - runner = run r - in destruct status runner err - process :: Response -> (Double -> Double -> String -> IO ()) -> IO () -> IO () process r run err = case info of @@ -49,80 +41,22 @@ process r run err = parts = (lines . unpack) body info = (readLines . take 4) parts -dumpCache :: Double -> Double -> String -> IO () -dumpCache lat lon tz = do - cache <- pathToCache "data.txt" - catch (writer cache) failure - prepareScripts lat lon - where - writer dir = writeFile dir (show lat ++ "\n" ++ show lon ++ "\n" ++ tz) - failure = print :: SomeException -> IO () - -readCache :: IO (Maybe (Double, Double, String)) -readCache = do - cache <- pathToCache "data.txt" - contents <- readFile cache - let entries = lines contents - case (entries !? 0, entries !? 1, entries !? 2) of - (Just lat, Just lon, Just tz) -> return (Just (read lat, read lon, tz)) - _ -> return Nothing - processRunner :: Response -> IO () -processRunner r = process r dumpCache backupRunner +processRunner r = process r run backupRunner + where run lat lon tz = dumpCache lat lon tz >>= prepareScripts -backupRunner :: IO () -backupRunner = do - contents <- readCache - case contents of - Nothing -> do - putStrLn "Failed to read cache after IP location failed" - crash - Just (lat, lon, _) -> prepareScripts lat lon - -start :: IO () -> IO () -start act = do - logFile <- pathToCache "log.txt" - existsLog <- doesFileExist logFile - if existsLog then do - contents <- readFile logFile - let entries = lines contents - (sequence_ . killall) entries - else createDirectoryIfMissing True (takeDirectory logFile) - act - -finish :: String -> IO () -finish queue = do - cache <- pathToCache "log.txt" - catch (writeFile cache num) failure - where - num = getId queue - getId = head . words . last . lines - failure = print :: SomeException -> IO () - -prepareScripts :: Double -> Double -> IO () -prepareScripts lat lon = do - sunriseTime <- sunriseNow lat lon - sunsetTime <- sunsetNow lat lon - lightMode <- activateOnSunrise sunriseTime sunsetTime - _ <- if lightMode then do - putStr "Light mode activation script scheduled for " - print sunriseTime - script <- pathToConfig lightModeScript - exec (buildCmd script sunriseTime) terminate - else do - putStrLn "Dark mode activation script scheduled for " - print sunsetTime - script <- pathToConfig darkModeScript - exec (buildCmd script sunsetTime) terminate - queue <- exec "atq" noQueue - finish queue - where - terminate _ = print "Scheduling process failed" >> return "" - noQueueMsg = "Scheduled process could not be retrieved (try rerunning if 'atq' fails)" - noQueue _ = print noQueueMsg >> return "" +ping :: (Response -> IO ()) -> IO () -> IO () +ping run err = do + res <- fetch query + case res of + Left e -> continue (toResponseMsg e) err + Right r -> + let status = (ResponseCode . responseStatus) r + runner = run r + in destruct status runner err routine :: IO () routine = ping processRunner backupRunner main :: IO () -main = start routine +main = start >> routine diff --git a/app/Sugar.hs b/app/Sugar.hs index 4c6658e..1d2213a 100644 --- a/app/Sugar.hs +++ b/app/Sugar.hs @@ -13,10 +13,13 @@ throw e = print e >> return mempty continue :: (Status s) => s -> IO () -> IO () continue e err = (putStrLn . disp) e >> err +failure :: SomeException -> IO () +failure = print + destruct :: (Status s) => s -> IO () -> IO () -> IO () -destruct status success failure +destruct status success unsuccessful | ok status = success - | otherwise = continue status failure + | otherwise = continue status unsuccessful crash :: IO () crash = (exitWith . ExitFailure) 1 diff --git a/app/Time.hs b/app/Time.hs index 2594007..93d37c8 100644 --- a/app/Time.hs +++ b/app/Time.hs @@ -1,6 +1,9 @@ module Time where -import Pure (sunriseNext) +import Pure (buildCmd, sunriseNext) +import Cache (pathToConfig) +import Const (darkModeScript, lightModeScript) +import Sugar (exec) import Data.Time (ZonedTime, getCurrentTime) import Data.Time.Solar (Location(Location), sunrise, sunset) @@ -26,3 +29,16 @@ sunsetNow lat lon = do activateOnSunrise :: ZonedTime -> ZonedTime -> IO Bool activateOnSunrise sunriseTime sunsetTime = sunriseNext sunriseTime sunsetTime <$> now + +activate :: String -> ZonedTime -> String -> IO String +activate msg time script = do + putStr msg + print time + scriptPath <- pathToConfig script + exec (buildCmd scriptPath time) terminate + where terminate _ = print "Scheduling process failed" >> return "" + +chooseActivation :: Bool -> ZonedTime -> ZonedTime -> IO String +chooseActivation lightMode sunriseTime sunsetTime + | lightMode = activate "Light mode activation script scheduled for " sunriseTime lightModeScript + | otherwise = activate "Dark mode activation script scheduled for " sunsetTime darkModeScript diff --git a/app/Workers.hs b/app/Workers.hs new file mode 100644 index 0000000..de22849 --- /dev/null +++ b/app/Workers.hs @@ -0,0 +1,26 @@ +module Workers where + +import Time (chooseActivation, activateOnSunrise, sunriseNow, sunsetNow) +import Cache (finish, readCache) +import Sugar (crash, exec) + +prepareScripts :: (Double, Double) -> IO () +prepareScripts (lat, lon) = do + sunriseTime <- sunriseNow lat lon + sunsetTime <- sunsetNow lat lon + lightMode <- activateOnSunrise sunriseTime sunsetTime + _ <- chooseActivation lightMode sunriseTime sunsetTime + queue <- exec "atq" noQueue + finish queue + where + noQueueMsg = "Scheduled process could not be retrieved (try rerunning if 'atq' fails)" + noQueue _ = print noQueueMsg >> return "" + +backupRunner :: IO () +backupRunner = do + contents <- readCache + case contents of + Nothing -> do + putStrLn "Failed to read cache after IP location failed" + crash + Just (lat, lon, _) -> prepareScripts (lat, lon) diff --git a/suntheme.cabal b/suntheme.cabal index 9499d09..e8a3179 100644 --- a/suntheme.cabal +++ b/suntheme.cabal @@ -62,7 +62,7 @@ executable suntheme main-is: Main.hs -- Modules included in this executable, other than Main. - other-modules: Types, Const, Pure, Time, Sugar, Getters + other-modules: Types, Const, Pure, Time, Sugar, Getters, Cache, Workers -- LANGUAGE extensions used by modules in this package. -- other-extensions: From 8c4f289a91f941347408308ec91335826a79c579 Mon Sep 17 00:00:00 2001 From: Youwen Wu Date: Fri, 16 Aug 2024 06:05:37 -0700 Subject: [PATCH 07/20] deps(dev): switch to nix --- .gitignore | 1 + Setup.hs | 6 + flake.lock | 707 +++++++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 53 ++++ suntheme.cabal | 2 +- 5 files changed, 768 insertions(+), 1 deletion(-) create mode 100644 Setup.hs create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.gitignore b/.gitignore index 4c9e245..ff2167c 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ cabal.project.local cabal.project.local~ .HTF/ .ghc.environment.* +result/ diff --git a/Setup.hs b/Setup.hs new file mode 100644 index 0000000..6fa548c --- /dev/null +++ b/Setup.hs @@ -0,0 +1,6 @@ +module Main (main) where + +import Distribution.Simple + +main :: IO () +main = defaultMain diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..f505f5a --- /dev/null +++ b/flake.lock @@ -0,0 +1,707 @@ +{ + "nodes": { + "HTTP": { + "flake": false, + "locked": { + "lastModified": 1451647621, + "narHash": "sha256-oHIyw3x0iKBexEo49YeUDV1k74ZtyYKGR2gNJXXRxts=", + "owner": "phadej", + "repo": "HTTP", + "rev": "9bc0996d412fef1787449d841277ef663ad9a915", + "type": "github" + }, + "original": { + "owner": "phadej", + "repo": "HTTP", + "type": "github" + } + }, + "cabal-32": { + "flake": false, + "locked": { + "lastModified": 1603716527, + "narHash": "sha256-X0TFfdD4KZpwl0Zr6x+PLxUt/VyKQfX7ylXHdmZIL+w=", + "owner": "haskell", + "repo": "cabal", + "rev": "48bf10787e27364730dd37a42b603cee8d6af7ee", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "3.2", + "repo": "cabal", + "type": "github" + } + }, + "cabal-34": { + "flake": false, + "locked": { + "lastModified": 1645834128, + "narHash": "sha256-wG3d+dOt14z8+ydz4SL7pwGfe7SiimxcD/LOuPCV6xM=", + "owner": "haskell", + "repo": "cabal", + "rev": "5ff598c67f53f7c4f48e31d722ba37172230c462", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "3.4", + "repo": "cabal", + "type": "github" + } + }, + "cabal-36": { + "flake": false, + "locked": { + "lastModified": 1669081697, + "narHash": "sha256-I5or+V7LZvMxfbYgZATU4awzkicBwwok4mVoje+sGmU=", + "owner": "haskell", + "repo": "cabal", + "rev": "8fd619e33d34924a94e691c5fea2c42f0fc7f144", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "3.6", + "repo": "cabal", + "type": "github" + } + }, + "cardano-shell": { + "flake": false, + "locked": { + "lastModified": 1608537748, + "narHash": "sha256-PulY1GfiMgKVnBci3ex4ptk2UNYMXqGjJOxcPy2KYT4=", + "owner": "input-output-hk", + "repo": "cardano-shell", + "rev": "9392c75087cb9a3d453998f4230930dea3a95725", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "cardano-shell", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1672831974, + "narHash": "sha256-z9k3MfslLjWQfnjBtEtJZdq3H7kyi2kQtUThfTgdRk0=", + "owner": "input-output-hk", + "repo": "flake-compat", + "rev": "45f2638735f8cdc40fe302742b79f248d23eb368", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "ref": "hkm/gitlab-fix", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "ghc-8.6.5-iohk": { + "flake": false, + "locked": { + "lastModified": 1600920045, + "narHash": "sha256-DO6kxJz248djebZLpSzTGD6s8WRpNI9BTwUeOf5RwY8=", + "owner": "input-output-hk", + "repo": "ghc", + "rev": "95713a6ecce4551240da7c96b6176f980af75cae", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "ref": "release/8.6.5-iohk", + "repo": "ghc", + "type": "github" + } + }, + "hackage": { + "flake": false, + "locked": { + "lastModified": 1723768183, + "narHash": "sha256-YG61ZL5YZvSWPvdusFsS8EYDJ/MW+EgX8fjNMWU18GQ=", + "owner": "input-output-hk", + "repo": "hackage.nix", + "rev": "37977e3b6201db1e0f3c62d9c55ec20dfcee10a2", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "hackage.nix", + "type": "github" + } + }, + "haskellNix": { + "inputs": { + "HTTP": "HTTP", + "cabal-32": "cabal-32", + "cabal-34": "cabal-34", + "cabal-36": "cabal-36", + "cardano-shell": "cardano-shell", + "flake-compat": "flake-compat", + "ghc-8.6.5-iohk": "ghc-8.6.5-iohk", + "hackage": "hackage", + "hls-1.10": "hls-1.10", + "hls-2.0": "hls-2.0", + "hls-2.2": "hls-2.2", + "hls-2.3": "hls-2.3", + "hls-2.4": "hls-2.4", + "hls-2.5": "hls-2.5", + "hls-2.6": "hls-2.6", + "hls-2.7": "hls-2.7", + "hls-2.8": "hls-2.8", + "hls-2.9": "hls-2.9", + "hpc-coveralls": "hpc-coveralls", + "hydra": "hydra", + "iserv-proxy": "iserv-proxy", + "nixpkgs": [ + "haskellNix", + "nixpkgs-unstable" + ], + "nixpkgs-2003": "nixpkgs-2003", + "nixpkgs-2105": "nixpkgs-2105", + "nixpkgs-2111": "nixpkgs-2111", + "nixpkgs-2205": "nixpkgs-2205", + "nixpkgs-2211": "nixpkgs-2211", + "nixpkgs-2305": "nixpkgs-2305", + "nixpkgs-2311": "nixpkgs-2311", + "nixpkgs-2405": "nixpkgs-2405", + "nixpkgs-unstable": "nixpkgs-unstable", + "old-ghc-nix": "old-ghc-nix", + "stackage": "stackage" + }, + "locked": { + "lastModified": 1723769442, + "narHash": "sha256-dGhNJril+hILFro59PdYWwKZYJ6/2A1hozTanaAu4lE=", + "owner": "input-output-hk", + "repo": "haskell.nix", + "rev": "517ceaa97e38ae211d9c311952cc8be4a4a026cd", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "haskell.nix", + "type": "github" + } + }, + "hls-1.10": { + "flake": false, + "locked": { + "lastModified": 1680000865, + "narHash": "sha256-rc7iiUAcrHxwRM/s0ErEsSPxOR3u8t7DvFeWlMycWgo=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "b08691db779f7a35ff322b71e72a12f6e3376fd9", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "1.10.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.0": { + "flake": false, + "locked": { + "lastModified": 1687698105, + "narHash": "sha256-OHXlgRzs/kuJH8q7Sxh507H+0Rb8b7VOiPAjcY9sM1k=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "783905f211ac63edf982dd1889c671653327e441", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.0.0.1", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.2": { + "flake": false, + "locked": { + "lastModified": 1693064058, + "narHash": "sha256-8DGIyz5GjuCFmohY6Fa79hHA/p1iIqubfJUTGQElbNk=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "b30f4b6cf5822f3112c35d14a0cba51f3fe23b85", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.2.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.3": { + "flake": false, + "locked": { + "lastModified": 1695910642, + "narHash": "sha256-tR58doOs3DncFehHwCLczJgntyG/zlsSd7DgDgMPOkI=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "458ccdb55c9ea22cd5d13ec3051aaefb295321be", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.3.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.4": { + "flake": false, + "locked": { + "lastModified": 1699862708, + "narHash": "sha256-YHXSkdz53zd0fYGIYOgLt6HrA0eaRJi9mXVqDgmvrjk=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "54507ef7e85fa8e9d0eb9a669832a3287ffccd57", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.4.0.1", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.5": { + "flake": false, + "locked": { + "lastModified": 1701080174, + "narHash": "sha256-fyiR9TaHGJIIR0UmcCb73Xv9TJq3ht2ioxQ2mT7kVdc=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "27f8c3d3892e38edaef5bea3870161815c4d014c", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.5.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.6": { + "flake": false, + "locked": { + "lastModified": 1705325287, + "narHash": "sha256-+P87oLdlPyMw8Mgoul7HMWdEvWP/fNlo8jyNtwME8E8=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "6e0b342fa0327e628610f2711f8c3e4eaaa08b1e", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.6.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.7": { + "flake": false, + "locked": { + "lastModified": 1708965829, + "narHash": "sha256-LfJ+TBcBFq/XKoiNI7pc4VoHg4WmuzsFxYJ3Fu+Jf+M=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "50322b0a4aefb27adc5ec42f5055aaa8f8e38001", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.7.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.8": { + "flake": false, + "locked": { + "lastModified": 1715153580, + "narHash": "sha256-Vi/iUt2pWyUJlo9VrYgTcbRviWE0cFO6rmGi9rmALw0=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "dd1be1beb16700de59e0d6801957290bcf956a0a", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.8.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.9": { + "flake": false, + "locked": { + "lastModified": 1718469202, + "narHash": "sha256-THXSz+iwB1yQQsr/PY151+2GvtoJnTIB2pIQ4OzfjD4=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "40891bccb235ebacce020b598b083eab9dda80f1", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.9.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hpc-coveralls": { + "flake": false, + "locked": { + "lastModified": 1607498076, + "narHash": "sha256-8uqsEtivphgZWYeUo5RDUhp6bO9j2vaaProQxHBltQk=", + "owner": "sevanspowell", + "repo": "hpc-coveralls", + "rev": "14df0f7d229f4cd2e79f8eabb1a740097fdfa430", + "type": "github" + }, + "original": { + "owner": "sevanspowell", + "repo": "hpc-coveralls", + "type": "github" + } + }, + "hydra": { + "inputs": { + "nix": "nix", + "nixpkgs": [ + "haskellNix", + "hydra", + "nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1671755331, + "narHash": "sha256-hXsgJj0Cy0ZiCiYdW2OdBz5WmFyOMKuw4zyxKpgUKm4=", + "owner": "NixOS", + "repo": "hydra", + "rev": "f48f00ee6d5727ae3e488cbf9ce157460853fea8", + "type": "github" + }, + "original": { + "id": "hydra", + "type": "indirect" + } + }, + "iserv-proxy": { + "flake": false, + "locked": { + "lastModified": 1717479972, + "narHash": "sha256-7vE3RQycHI1YT9LHJ1/fUaeln2vIpYm6Mmn8FTpYeVo=", + "owner": "stable-haskell", + "repo": "iserv-proxy", + "rev": "2ed34002247213fc435d0062350b91bab920626e", + "type": "github" + }, + "original": { + "owner": "stable-haskell", + "ref": "iserv-syms", + "repo": "iserv-proxy", + "type": "github" + } + }, + "lowdown-src": { + "flake": false, + "locked": { + "lastModified": 1633514407, + "narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=", + "owner": "kristapsdz", + "repo": "lowdown", + "rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8", + "type": "github" + }, + "original": { + "owner": "kristapsdz", + "repo": "lowdown", + "type": "github" + } + }, + "nix": { + "inputs": { + "lowdown-src": "lowdown-src", + "nixpkgs": "nixpkgs", + "nixpkgs-regression": "nixpkgs-regression" + }, + "locked": { + "lastModified": 1661606874, + "narHash": "sha256-9+rpYzI+SmxJn+EbYxjGv68Ucp22bdFUSy/4LkHkkDQ=", + "owner": "NixOS", + "repo": "nix", + "rev": "11e45768b34fdafdcf019ddbd337afa16127ff0f", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "2.11.0", + "repo": "nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1657693803, + "narHash": "sha256-G++2CJ9u0E7NNTAi9n5G8TdDmGJXcIjkJ3NF8cetQB8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "365e1b3a859281cf11b94f87231adeabbdd878a2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-22.05-small", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-2003": { + "locked": { + "lastModified": 1620055814, + "narHash": "sha256-8LEHoYSJiL901bTMVatq+rf8y7QtWuZhwwpKE2fyaRY=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "1db42b7fe3878f3f5f7a4f2dc210772fd080e205", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-20.03-darwin", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-2105": { + "locked": { + "lastModified": 1659914493, + "narHash": "sha256-lkA5X3VNMKirvA+SUzvEhfA7XquWLci+CGi505YFAIs=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "022caabb5f2265ad4006c1fa5b1ebe69fb0c3faf", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-21.05-darwin", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-2111": { + "locked": { + "lastModified": 1659446231, + "narHash": "sha256-hekabNdTdgR/iLsgce5TGWmfIDZ86qjPhxDg/8TlzhE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "eabc38219184cc3e04a974fe31857d8e0eac098d", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-21.11-darwin", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-2205": { + "locked": { + "lastModified": 1685573264, + "narHash": "sha256-Zffu01pONhs/pqH07cjlF10NnMDLok8ix5Uk4rhOnZQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "380be19fbd2d9079f677978361792cb25e8a3635", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-22.05-darwin", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-2211": { + "locked": { + "lastModified": 1688392541, + "narHash": "sha256-lHrKvEkCPTUO+7tPfjIcb7Trk6k31rz18vkyqmkeJfY=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "ea4c80b39be4c09702b0cb3b42eab59e2ba4f24b", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-22.11-darwin", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-2305": { + "locked": { + "lastModified": 1705033721, + "narHash": "sha256-K5eJHmL1/kev6WuqyqqbS1cdNnSidIZ3jeqJ7GbrYnQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a1982c92d8980a0114372973cbdfe0a307f1bdea", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-23.05-darwin", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-2311": { + "locked": { + "lastModified": 1719957072, + "narHash": "sha256-gvFhEf5nszouwLAkT9nWsDzocUTqLWHuL++dvNjMp9I=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "7144d6241f02d171d25fba3edeaf15e0f2592105", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-23.11-darwin", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-2405": { + "locked": { + "lastModified": 1720122915, + "narHash": "sha256-Nby8WWxj0elBu1xuRaUcRjPi/rU3xVbkAt2kj4QwX2U=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "835cf2d3f37989c5db6585a28de967a667a75fb1", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-24.05-darwin", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-regression": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, + "nixpkgs-unstable": { + "locked": { + "lastModified": 1720181791, + "narHash": "sha256-i4vJL12/AdyuQuviMMd1Hk2tsGt02hDNhA0Zj1m16N8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "4284c2b73c8bce4b46a6adf23e16d9e2ec8da4bb", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "old-ghc-nix": { + "flake": false, + "locked": { + "lastModified": 1631092763, + "narHash": "sha256-sIKgO+z7tj4lw3u6oBZxqIhDrzSkvpHtv0Kki+lh9Fg=", + "owner": "angerman", + "repo": "old-ghc-nix", + "rev": "af48a7a7353e418119b6dfe3cd1463a657f342b8", + "type": "github" + }, + "original": { + "owner": "angerman", + "ref": "master", + "repo": "old-ghc-nix", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "haskellNix": "haskellNix", + "nixpkgs": [ + "haskellNix", + "nixpkgs-unstable" + ] + } + }, + "stackage": { + "flake": false, + "locked": { + "lastModified": 1723594352, + "narHash": "sha256-cQVhF1M1et3/XNE1sclwH39prxIDMUojTdnW61t3YrM=", + "owner": "input-output-hk", + "repo": "stackage.nix", + "rev": "c077da02c56031a78adc4bf0cf2b182effc895ed", + "type": "github" + }, + "original": { + "owner": "input-output-hk", + "repo": "stackage.nix", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..8455d1a --- /dev/null +++ b/flake.nix @@ -0,0 +1,53 @@ +{ + description = "Flake for suntheme's development environment and builds"; + + inputs.haskellNix.url = "github:input-output-hk/haskell.nix"; + inputs.nixpkgs.follows = "haskellNix/nixpkgs-unstable"; + inputs.flake-utils.url = "github:numtide/flake-utils"; + outputs = { + self, + nixpkgs, + flake-utils, + haskellNix, + }: let + supportedSystems = [ + "x86_64-linux" + "x86_64-darwin" + "aarch64-linux" + "aarch64-darwin" + ]; + in + flake-utils.lib.eachSystem supportedSystems (system: let + overlays = [ + haskellNix.overlay + (final: prev: { + suntheme = final.haskell-nix.project' { + src = ./.; + compiler-nix-name = "ghc982"; + shell.tools = { + cabal = {}; + hlint = {}; + haskell-language-server = {}; + }; + }; + }) + ]; + pkgs = import nixpkgs { + inherit system overlays; + inherit (haskellNix) config; + }; + flake = pkgs.suntheme.flake {}; + in + flake + // { + formatter = pkgs.alejandra; + + packages.default = flake.packages."suntheme:exe:suntheme"; + }); + + nixConfig = { + extra-substituters = ["https://cache.iog.io"]; + extra-trusted-public-keys = ["hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ="]; + allow-import-from-derivation = "true"; + }; +} diff --git a/suntheme.cabal b/suntheme.cabal index e8a3179..206cc85 100644 --- a/suntheme.cabal +++ b/suntheme.cabal @@ -68,7 +68,7 @@ executable suntheme -- other-extensions: -- Other library packages from which modules are imported. - build-depends: base ^>=4.17.2.1, + build-depends: base ^>=4.19.1.0, request ^>=0.2.2.0, process ^>=1.6.18.0, bytestring ^>=0.11.5.3, From 2bf47c02ec6c7f54931fe2a552b1c7efcd741c77 Mon Sep 17 00:00:00 2001 From: Youwen Wu Date: Fri, 16 Aug 2024 06:33:44 -0700 Subject: [PATCH 08/20] fix: delete unused Setup.hs file --- Setup.hs | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 Setup.hs diff --git a/Setup.hs b/Setup.hs deleted file mode 100644 index 6fa548c..0000000 --- a/Setup.hs +++ /dev/null @@ -1,6 +0,0 @@ -module Main (main) where - -import Distribution.Simple - -main :: IO () -main = defaultMain From e86fbe9d5a69a3b72fbacd862cefc4c93687ac00 Mon Sep 17 00:00:00 2001 From: Youwen Wu Date: Fri, 16 Aug 2024 06:40:37 -0700 Subject: [PATCH 09/20] docs: add readme --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..82ef431 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# Suntheme + +runs a script on sunrise and sunset, written in pure haskell. + +You may be wondering how a program written in Haskell, the purely functional +programming language, could possibly act on the real world by running a so-called "script". + +It's simple. We take in the entire World as an input to a pure function, the IO Monad. +It then maps the original World to the changed World, with our desired IO actions carefully applied. + +> From the second perspective, an IO action transforms the whole world. IO actions are actually pure, because they receive a unique world as an argument and then return the changed world. + +See [this](https://lean-lang.org/functional_programming_in_lean/monads/io.html) for more information. From 2b541de07923aa4fe0109a6f23d4e405e413bc4d Mon Sep 17 00:00:00 2001 From: Youwen Wu Date: Fri, 16 Aug 2024 06:51:01 -0700 Subject: [PATCH 10/20] docs: add development instructions --- README.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/README.md b/README.md index 82ef431..0768ca0 100644 --- a/README.md +++ b/README.md @@ -11,3 +11,45 @@ It then maps the original World to the changed World, with our desired IO action > From the second perspective, an IO action transforms the whole world. IO actions are actually pure, because they receive a unique world as an argument and then return the changed world. See [this](https://lean-lang.org/functional_programming_in_lean/monads/io.html) for more information. + +## Hacking on suntheme + +It's trivially easy to get started with suntheme development thanks to [Nix](https://nixos.org/), the purely functional package manager. +Naturally, we leverage it as our primary package manager, for both Hackage packages and development tools like language servers and the like. + +First, install Nix through your preferred avenue. If unsure, we recommend [the Determinate Nix Installer](https://github.com/DeterminateSystems/nix-installer). +Make sure flakes and nix-command are enabled (the Determinate Installer will enable them by default). + +Once you have `nix`, simply type + +```bash +nix develop +``` + +Say yes to any prompts asking you to allow substituters or trust public keys. +Nix will fetch all of the required packages, such as GHC and Hackage dependencies. +Additionally, you will have access to `hlint` and the `haskell-language-server`. + +To create a build, type + +```bash +nix build +``` + +A binary will be produced in `result/bin/suntheme`. + +To make `suntheme` available in the shell without outputting to `result`, use + +```bash +nix shell +``` + +This will build a binary just like `nix build` but add it temporarily to the PATH, so you can just type `suntheme`. + +To build and run `suntheme` immediately without adding it to the PATH, use + +```bash +nix run +``` + +This will build a binary just like `nix shell` but immediately execute it. From 76ef994924efa4852a75f36babb0a19687e60a2a Mon Sep 17 00:00:00 2001 From: Youwen Wu Date: Fri, 16 Aug 2024 14:37:56 -0700 Subject: [PATCH 11/20] chore: add .envrc for automatic dev env loading --- .envrc | 1 + .gitignore | 1 + 2 files changed, 2 insertions(+) create mode 100644 .envrc diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore index ff2167c..a147f3a 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ cabal.project.local~ .HTF/ .ghc.environment.* result/ +.direnv/ From 06969ebdf7c48fe40222201d149ae487b274a19f Mon Sep 17 00:00:00 2001 From: Youwen Wu Date: Fri, 16 Aug 2024 14:38:11 -0700 Subject: [PATCH 12/20] deps: add all required dependencies to flake --- flake.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/flake.nix b/flake.nix index 8455d1a..c6af4b4 100644 --- a/flake.nix +++ b/flake.nix @@ -29,6 +29,10 @@ hlint = {}; haskell-language-server = {}; }; + shell.buildInputs = with pkgs; [ + at + busybox + ]; }; }) ]; From ff8148649a18d396b466cf59986cacbe7cd365bb Mon Sep 17 00:00:00 2001 From: Youwen Wu Date: Fri, 16 Aug 2024 14:38:35 -0700 Subject: [PATCH 13/20] fix: correctly gitignore nix result --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index a147f3a..f512922 100644 --- a/.gitignore +++ b/.gitignore @@ -21,5 +21,5 @@ cabal.project.local cabal.project.local~ .HTF/ .ghc.environment.* -result/ +result .direnv/ From dd63938643a65c13d2ae0f556e43941266304832 Mon Sep 17 00:00:00 2001 From: q9i <46249765+quantum9Innovation@users.noreply.github.com> Date: Fri, 16 Aug 2024 14:32:02 -0700 Subject: [PATCH 14/20] docs: formalize readme in preparation for merge --- README.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 0768ca0..bdfec74 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,14 @@ # Suntheme -runs a script on sunrise and sunset, written in pure haskell. +Runs a script on sunrise and sunset, written in pure Haskell + +--- You may be wondering how a program written in Haskell, the purely functional -programming language, could possibly act on the real world by running a so-called "script". +programming language, could possibly act on the real world by running a so-called "script." It's simple. We take in the entire World as an input to a pure function, the IO Monad. -It then maps the original World to the changed World, with our desired IO actions carefully applied. +It then maps the original World to a new (generated) World, with our desired IO actions carefully applied with mathematical precision. > From the second perspective, an IO action transforms the whole world. IO actions are actually pure, because they receive a unique world as an argument and then return the changed world. @@ -14,13 +16,13 @@ See [this](https://lean-lang.org/functional_programming_in_lean/monads/io.html) ## Hacking on suntheme -It's trivially easy to get started with suntheme development thanks to [Nix](https://nixos.org/), the purely functional package manager. -Naturally, we leverage it as our primary package manager, for both Hackage packages and development tools like language servers and the like. +It's trivial to get started with suntheme development thanks to [Nix](https://nixos.org/), the purely functional package manager. +Naturally, we leverage it as our primary package manager, both for Hackage and development tools like language servers and the like. -First, install Nix through your preferred avenue. If unsure, we recommend [the Determinate Nix Installer](https://github.com/DeterminateSystems/nix-installer). +First, install Nix through your preferred avenue or local system administrator. If unsure, we recommend [the Determinate Nix Installer](https://github.com/DeterminateSystems/nix-installer). Make sure flakes and nix-command are enabled (the Determinate Installer will enable them by default). -Once you have `nix`, simply type +Once you have `nix`, simply type: ```bash nix develop From d81ddc5a72ee95fbd2991cfec76b8ac2c0303f51 Mon Sep 17 00:00:00 2001 From: q9i <46249765+quantum9Innovation@users.noreply.github.com> Date: Fri, 16 Aug 2024 14:49:15 -0700 Subject: [PATCH 15/20] fix: optimize external dependency specifications remove busybox (pure bloat) and unnecessary documentation --- app/Main.hs | 4 +--- flake.nix | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/app/Main.hs b/app/Main.hs index 9d4ac6b..8b933a8 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -1,7 +1,5 @@ --- requires 'at' for running a command at a certain time --- requires 'date' for converting unix time to human readable time -- run on boot and at noon and midnight --- don't add commands to at while this script is running (monadic purity must be preserved) +-- don't add commands to `at` while this script is running (monadic purity must be preserved) module Main where diff --git a/flake.nix b/flake.nix index c6af4b4..e0bd1d9 100644 --- a/flake.nix +++ b/flake.nix @@ -1,5 +1,5 @@ { - description = "Flake for suntheme's development environment and builds"; + description = "Suntheme's development and build environment"; inputs.haskellNix.url = "github:input-output-hk/haskell.nix"; inputs.nixpkgs.follows = "haskellNix/nixpkgs-unstable"; @@ -31,7 +31,6 @@ }; shell.buildInputs = with pkgs; [ at - busybox ]; }; }) From 6aa64ac73b3abd91f6444c3ef7ca278899195441 Mon Sep 17 00:00:00 2001 From: Youwen Wu <38934577+youwen5@users.noreply.github.com> Date: Fri, 16 Aug 2024 14:44:21 -0700 Subject: [PATCH 16/20] ci: add cachix tests --- .github/workflows/main.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..caba877 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,18 @@ +name: "Test" +on: + pull_request: + push: +jobs: + tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: cachix/install-nix-action@v25 + with: + nix_path: nixpkgs=channel:nixos-unstable + - uses: cachix/cachix-action@v14 + with: + name: suntheme + authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' + - run: nix build + - run: nix develop From 4647657666119bd17de070bec5bbf9158bdf43f6 Mon Sep 17 00:00:00 2001 From: Youwen Wu Date: Fri, 16 Aug 2024 14:50:41 -0700 Subject: [PATCH 17/20] fix: allow flake binary cache and substituters --- .github/workflows/main.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index caba877..3f6b038 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,13 +6,13 @@ jobs: tests: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: cachix/install-nix-action@v25 - with: - nix_path: nixpkgs=channel:nixos-unstable - - uses: cachix/cachix-action@v14 - with: - name: suntheme - authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - - run: nix build - - run: nix develop + - uses: actions/checkout@v4 + - uses: cachix/install-nix-action@v25 + with: + nix_path: nixpkgs=channel:nixos-unstable + - uses: cachix/cachix-action@v14 + with: + name: suntheme + authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" + - run: nix build --accept-flake-config + - run: nix develop From da93405e5f6a10d87a0e78a84e8ca679f3af3aa0 Mon Sep 17 00:00:00 2001 From: Youwen Wu Date: Fri, 16 Aug 2024 14:52:44 -0700 Subject: [PATCH 18/20] deps: add suntheme cachix to flake --- flake.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flake.nix b/flake.nix index e0bd1d9..c985748 100644 --- a/flake.nix +++ b/flake.nix @@ -49,8 +49,8 @@ }); nixConfig = { - extra-substituters = ["https://cache.iog.io"]; - extra-trusted-public-keys = ["hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ="]; + extra-substituters = ["https://cache.iog.io" "https://suntheme.cachix.org"]; + extra-trusted-public-keys = ["hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ=" "suntheme.cachix.org-1:fHjlz7YAmMUcLp3tsZis8g9wIsDS6HvECGR3uZETGRo="]; allow-import-from-derivation = "true"; }; } From dde48b1109646c6d6fc6dfcb8b388de95500bb8d Mon Sep 17 00:00:00 2001 From: Youwen Wu Date: Fri, 16 Aug 2024 14:55:44 -0700 Subject: [PATCH 19/20] fix: accept flake config for nix develop --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3f6b038..592fc95 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,4 +15,4 @@ jobs: name: suntheme authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" - run: nix build --accept-flake-config - - run: nix develop + - run: nix develop --accept-flake-config From d3fb14e4362b427a033904c1e3e9dc28f5c8c405 Mon Sep 17 00:00:00 2001 From: q9i <46249765+quantum9Innovation@users.noreply.github.com> Date: Fri, 16 Aug 2024 18:25:46 -0700 Subject: [PATCH 20/20] docs(workflow): rename main.yml --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 592fc95..09118b1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,4 +1,4 @@ -name: "Test" +name: "Cache binaries" on: pull_request: push: