Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@
`lineman` uses [PVP Versioning][1].
The changelog is available [on GitHub][2].

## 1.0.5

* Restructure modules
* Differ raw and domain types
* Add option for swarm concurrency
* Prevent processing of invalid paths and absent targets
* Add delay option to config
* Use clear namings
* Update README

## 1.0.4

* Land on `typed-base`
Expand Down
22 changes: 18 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,22 @@ And either

Lineman uses [dhall](https://dhall-lang.org) configuration. See [Config](./lineman.dhall) example.

According to the config `lineman`:
- traverses target directory recursively.
- finds directories that have `lineman.cabal` file.
- apply `mkdir test_dir` in the found directories.
## Term policy

- `Action` - a single command that run in a directory due to particular `conditions`
- `Conditions` describe the directory has to have to run `action`
- `EntryPoint` is a parent directory where `lineman` starts seeking for `targets`
- `Target` is a directory that aligns to `condition`
- `Swarm` is a bunch of `actions` that match to `condition`
- `Hive` is a collection of `swarms`

## Features

- Both `actions` in a `swarm` and `swarms` in a `hive` can be run concurrently or successively
- Both `actions` and `swarms` can be interspersed with breaks
- `Lineman` starts seeking `targets` from `entryPoint` recursively and finds directories that have particular subdirectories, files or extensions. And then it run `Action` in found `targets`.

## Use cases

- Clear build artefact in bunch of projects.

3 changes: 3 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ install:
build:
cabal build

run:
cabal run lineman -- ./lineman.dhall

# update the bounds of dependencies
update:
cabal-bounds update
Expand Down
8 changes: 4 additions & 4 deletions lineman.cabal
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cabal-version: 3.0
name: lineman
version: 1.0.4
version: 1.0.5
synopsis: traverse directory and run command
description:
Lineman traverses directory recursively and run command by condition
Expand Down Expand Up @@ -54,18 +54,18 @@ library
hs-source-dirs: src
exposed-modules:
App
Cook
Type.Raw
Type.Domain
Parser
Concurrent
Lineman
Log
Types

build-depends:
, containers
, dhall
, directory
, extra
, filepath
, katip
, ki
, monad-control
Expand Down
91 changes: 63 additions & 28 deletions lineman.dhall
Original file line number Diff line number Diff line change
@@ -1,37 +1,72 @@
let Condition : Type =
{ hasFiles : List Text
, hasDirectories : List Text
, hasExtensions : List Text
, command : Text
, args : List Text
}

let condition1 : Condition =
{ hasFiles = ["stack.yaml"] : List Text
-- ^ Target directory has files
, hasDirectories = [".exercism"] : List Text
-- ^ Target directory has directories
, hasExtensions = [] : List Text
-- ^ Target directory has extensions.
let RawCondition : Type =
{ rcIndex : Natural
-- Arbitrary index
, rcEntryPoint : Text
-- ^ first target where lineman starts recursively from.
-- target consume 'rel', 'abs' and '~'' paths
, rcHasFiles : List Text
-- ^ Target directory has files that have to be relative to target path.
, rcHasDirectories : List Text
-- ^ Target directory has directories that have to be relative to target path
, rcHasExtensions : List Text
-- ^ Target directory has files with these extensions.
-- It consume exts with and without '.'
, command = "ls"
, rcCommand : Text
-- ^ Command to run in searched directories
, args = [] : List Text
, rcArgs : List Text
-- ^ Command's arguments
, rcConcurrentAgents : Bool
-- ^ run actions concurrently within the particular condition
, rcBreakBetweenAgents : Double
-- ^ Interval between actions in seconds
}

let condition_1 : RawCondition =
{ rcIndex = 1
, rcEntryPoint = "~/source/test/"
, rcHasFiles = ["a/log"] : List Text
, rcHasDirectories = [] : List Text
, rcHasExtensions = [] : List Text
, rcCommand = "touch"
, rcArgs = ["readme.txt"] : List Text
, rcConcurrentAgents = False
, rcBreakBetweenAgents = 1.0
}

let condition_2 : RawCondition =
{ rcIndex = 2
, rcEntryPoint = "~/source/test/"
, rcHasFiles = ["a/log"] : List Text
, rcHasDirectories = [] : List Text
, rcHasExtensions = [] : List Text
, rcCommand = "rm"
, rcArgs = ["readme.txt"] : List Text
, rcConcurrentAgents = False
, rcBreakBetweenAgents = 1.0
}

let Verbosity : Type = < V0 | V1 | V2 | V3 >
-- ^ levels of verbosity
let Severity : Type =
< DebugS | InfoS | NoticeS | WarningS | ErrorS | CriticalS | AlertS | EmergencyS >

in { cTarget = "your/path"
-- ^ target where you plan that the lineman recursively starts from.
-- target consume 'rel', 'abs' and '~'' paths
, cConditions = [ condition1 ] : List Condition
-- ^ within the target one can run several commands with its own conditions
, cAsync = False
-- ^ make lineman to work concurrently
, cSeverity = Severity.DebugS
, cVerbosity = Verbosity.V0
}
let Config : Type =
{ confRawConditions : List RawCondition
-- ^ within the target it is possible to run several commands with own conditions
, confSeverity : Severity
, confVerbosity : Verbosity
-- ^ level of verbosity
, confConcurrentSwarms : Bool
-- ^ run the swarm of actions concurrently
, confBreakBetweenSwarms : Double
-- ^ add delay of running next batch of actions (in seconds)
}

let config : Config =
{ confRawConditions = [ condition_1, condition_2 ] : List RawCondition
, confSeverity = Severity.DebugS
, confVerbosity = Verbosity.V0
, confConcurrentSwarms = False
, confBreakBetweenSwarms = 5.0
}

in config
41 changes: 20 additions & 21 deletions src/App.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ module App (
appLineman,
) where

import Cook (safeHead)
import Lineman (launchAction)
import Lineman (launchSwarm)
import Log (mkLogEnv)
import Types (App (unApp), Config (..), Env (..))
import Concurrent (forConcurrentlyKi)
import Parser (prepareConditions, safeHead)
import Type.Domain (App (unApp), Env (..))
import Type.Raw (Config (..))

-- import Control.Concurrent.Async.Lifted (forConcurrently)
import Control.Exception.Safe (throwIO, tryAny)
import Control.Monad (forM, when)
import Control.Monad (when)
import Control.Monad.Reader (ReaderT (..))
import Dhall (auto, inputFile)
import System.Environment (getArgs)
Expand All @@ -27,20 +26,21 @@ appLineman = do
pPrintString "Launch command with that Config? (yes/no)"
str <- getLine
when (str == "yes") $ do
logEnv <- mkLogEnv (cVerbosity config) (cSeverity config)
let env =
Env
{ envLogEnv = logEnv
, envActionMode =
if cAsync config
then forConcurrentlyKi
else forM
, envLogContext = mempty
, envLogNamespace = mempty
, envTarget = cTarget config
, envConditions = cConditions config
}
runApp env launchAction
mConditions <- prepareConditions $ confRawConditions config
case mConditions of
Nothing -> pPrintString "No conditions found in config file for running lineman"
Just conditions -> do
logEnv <- mkLogEnv (confVerbosity config) (confSeverity config)
let env =
Env
{ envLogEnv = logEnv
, envLogContext = mempty
, envLogNamespace = mempty
, envConditions = conditions
, envSwarmConcurrent = confConcurrentSwarms config
, envSwarmBreak = confBreakBetweenSwarms config
}
runApp env launchSwarm

runApp :: Env -> App a -> IO a
runApp env app = runReaderT (unApp app) env
Expand All @@ -53,4 +53,3 @@ getConfig path = do
pPrintString "Config parsing failed"
throwIO err
Right decoded -> pure decoded

16 changes: 13 additions & 3 deletions src/Concurrent.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

module Concurrent (
forConcurrentlyKi,
forConcurrentlyKi_,
) where

import Control.Concurrent.STM (atomically)
Expand All @@ -13,14 +14,23 @@ import Control.Monad.Trans.Control (MonadBaseControl, StM, control)
import Ki

forConcurrentlyKi
:: (MonadBaseControl IO m, StM m (Ki.Thread b) ~ Ki.Thread b, StM m b ~ b, MonadIO m)
=> [a]
:: (Traversable t, MonadBaseControl IO m, StM m (Ki.Thread b) ~ Ki.Thread b, StM m b ~ b, MonadIO m)
=> t a
-> (a -> m b)
-> m [b]
-> m (t b)
forConcurrentlyKi ns f = control $ \unlift -> scopedM \scope -> unlift $ do
threads <- mapM (forkM scope . f) ns
mapM (liftBase . atomically . Ki.await) threads

forConcurrentlyKi_
:: (Traversable t, MonadBaseControl IO m, StM m (Ki.Thread b) ~ Ki.Thread b, StM m b ~ b, MonadIO m)
=> t a
-> (a -> m b)
-> m ()
forConcurrentlyKi_ ns f = control $ \unlift -> scopedM \scope -> unlift $ do
threads <- mapM (forkM scope . f) ns
mapM_ (liftBase . atomically . Ki.await) threads

forkM
:: (MonadBaseControl IO m, StM m (Ki.Thread a) ~ Ki.Thread a, StM m a ~ a)
=> Ki.Scope
Expand Down
78 changes: 0 additions & 78 deletions src/Cook.hs

This file was deleted.

Loading