mirror of
https://github.com/quantum9Innovation/suntheme.git
synced 2024-11-25 01:43:50 -08:00
Compare commits
20 commits
546de7273f
...
48de041a44
Author | SHA1 | Date | |
---|---|---|---|
|
48de041a44 | ||
7342a97eb4 | |||
49169b914a | |||
cc418491bc | |||
|
e96156a936 | ||
|
4ea4e5e716 | ||
|
42af799d0f | ||
853fcc790a | |||
bb35415ed0 | |||
e6dc490044 | |||
c40d3b8d4a | |||
f79b895cc3 | |||
61895a9abb | |||
012a42a7d3 | |||
|
3a8fb784b8 | ||
|
13188fe64d | ||
|
b9750dafea | ||
|
1ed2a75f6c | ||
|
cc1935f92c | ||
|
6cf30685ad |
16 changed files with 1120 additions and 226 deletions
1
.envrc
Normal file
1
.envrc
Normal file
|
@ -0,0 +1 @@
|
|||
use flake
|
18
.github/workflows/main.yml
vendored
Normal file
18
.github/workflows/main.yml
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
name: "Cache binaries"
|
||||
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 --accept-flake-config
|
||||
- run: nix develop --accept-flake-config
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -21,3 +21,5 @@ cabal.project.local
|
|||
cabal.project.local~
|
||||
.HTF/
|
||||
.ghc.environment.*
|
||||
result
|
||||
.direnv/
|
||||
|
|
57
README.md
Normal file
57
README.md
Normal file
|
@ -0,0 +1,57 @@
|
|||
# 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 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.
|
||||
|
||||
See [this](https://lean-lang.org/functional_programming_in_lean/monads/io.html) for more information.
|
||||
|
||||
## Hacking on suntheme
|
||||
|
||||
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 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:
|
||||
|
||||
```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.
|
51
app/Cache.hs
Normal file
51
app/Cache.hs
Normal file
|
@ -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
|
19
app/Const.hs
Normal file
19
app/Const.hs
Normal file
|
@ -0,0 +1,19 @@
|
|||
module Const where
|
||||
|
||||
query :: String
|
||||
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"
|
||||
|
||||
darkModeScript :: String
|
||||
darkModeScript = "dark.sh"
|
7
app/Getters.hs
Normal file
7
app/Getters.hs
Normal file
|
@ -0,0 +1,7 @@
|
|||
module Getters where
|
||||
|
||||
import Control.Exception (SomeException, try)
|
||||
import Network.HTTP.Request (Response, get)
|
||||
|
||||
fetch :: String -> IO (Either SomeException Response)
|
||||
fetch = try . get
|
245
app/Main.hs
245
app/Main.hs
|
@ -1,128 +1,35 @@
|
|||
-- 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
|
||||
|
||||
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 Pure (readLines)
|
||||
import Cache (dumpCache, start)
|
||||
import Const (query)
|
||||
import Sugar (continue, destruct)
|
||||
import Types (ResponseCode(..), ResponseMsg(..), genericErr, toResponseMsg)
|
||||
import Getters (fetch)
|
||||
import Workers (backupRunner, prepareScripts)
|
||||
|
||||
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)
|
||||
import Network.HTTP.Request (Response(responseBody, responseStatus), get)
|
||||
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!
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
|
||||
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
|
||||
Right r ->
|
||||
let status = (ResponseCode . responseStatus) r
|
||||
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
|
||||
Nothing -> cont genericErr err
|
||||
Nothing -> continue genericErr err
|
||||
Just (msg, lat, lon, tz) ->
|
||||
let status = ResponseMsg msg
|
||||
runner = run lat lon tz
|
||||
|
@ -132,114 +39,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
|
||||
|
||||
crash :: IO ()
|
||||
crash = exitWith (ExitFailure 1)
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
dispatch cmd = exec cmd failure
|
||||
failure :: SomeException -> IO String
|
||||
failure e = print e >> return []
|
||||
|
||||
start :: IO () -> IO ()
|
||||
start act = do
|
||||
logFile <- pathToCache "log.txt"
|
||||
existsLog <- doesFileExist logFile
|
||||
if existsLog then do
|
||||
contents <- readFile logFile
|
||||
let entries = lines contents
|
||||
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 ()
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
|
|
28
app/Pure.hs
Normal file
28
app/Pure.hs
Normal file
|
@ -0,0 +1,28 @@
|
|||
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)
|
||||
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
|
||||
|
||||
sunriseNext :: ZonedTime -> ZonedTime -> ZonedTime -> Bool
|
||||
sunriseNext sunriseTime sunsetTime time =
|
||||
let utcTimeNow = zonedTimeToUTC time
|
||||
utcSunrise = zonedTimeToUTC sunriseTime
|
||||
utcSunset = zonedTimeToUTC sunsetTime
|
||||
in utcTimeNow < utcSunrise || utcTimeNow > utcSunset
|
31
app/Sugar.hs
Normal file
31
app/Sugar.hs
Normal file
|
@ -0,0 +1,31 @@
|
|||
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
|
||||
|
||||
failure :: SomeException -> IO ()
|
||||
failure = print
|
||||
|
||||
destruct :: (Status s) => s -> IO () -> IO () -> IO ()
|
||||
destruct status success unsuccessful
|
||||
| ok status = success
|
||||
| otherwise = continue status unsuccessful
|
||||
|
||||
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
|
44
app/Time.hs
Normal file
44
app/Time.hs
Normal file
|
@ -0,0 +1,44 @@
|
|||
module Time where
|
||||
|
||||
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)
|
||||
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
|
||||
|
||||
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
|
32
app/Types.hs
Normal file
32
app/Types.hs
Normal file
|
@ -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"
|
26
app/Workers.hs
Normal file
26
app/Workers.hs
Normal file
|
@ -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)
|
707
flake.lock
Normal file
707
flake.lock
Normal file
|
@ -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
|
||||
}
|
56
flake.nix
Normal file
56
flake.nix
Normal file
|
@ -0,0 +1,56 @@
|
|||
{
|
||||
description = "Suntheme's development and build environment";
|
||||
|
||||
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 = {};
|
||||
};
|
||||
shell.buildInputs = with pkgs; [
|
||||
at
|
||||
];
|
||||
};
|
||||
})
|
||||
];
|
||||
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" "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";
|
||||
};
|
||||
}
|
|
@ -62,13 +62,13 @@ executable suntheme
|
|||
main-is: Main.hs
|
||||
|
||||
-- Modules included in this executable, other than Main.
|
||||
-- other-modules:
|
||||
other-modules: Types, Const, Pure, Time, Sugar, Getters, Cache, Workers
|
||||
|
||||
-- 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,
|
||||
build-depends: base ^>=4.19.1.0,
|
||||
request ^>=0.2.2.0,
|
||||
process ^>=1.6.18.0,
|
||||
bytestring ^>=0.11.5.3,
|
||||
|
|
Loading…
Reference in a new issue