Git Product home page Git Product logo

iris's Introduction

Hi there, I'm Dmitrii (he/him) πŸ‘‹

Website YouTube Twitter LinkedIn GitHub sponsorship

I'm a Senior Software Engineer at Bloomberg using OCaml πŸͺ.

For the last 8 years, I've been using Functional Programming (OCaml, Haskell, Elm, PureScript) in various industries including healthcare, education, dating, fintech, and blockchain.

I was a member of the Haskell Core Libraries Committee in 2023.

My passion is making Functional Programming and OCaml more accessible to people with diverse backgrounds. Check out my free Haskell Course for complete beginners!

I love learning new technologies and developing new skills, so I'm currently in the process of exploring OCaml, Rust, Elm, and Kotlin.


Subscribe to my mailing list πŸ“₯ if you want to be among the first people to hear about announcements of my books, courses, projects and much more!

My work

I do all kinds of work to help people get started with FP as easier as possible:

  • πŸŽ₯ YouTube β€” FP, Haskell, tech
  • πŸ“š Blog β€” articles explaining idiomatic ways to solve problems using FP and Haskell
  • :octocat: OSS β€” my projects, courses, frameworks, dev tools

OSS has been a big part of my educator and mentor journey for many years and I'd like to highlight a few notable projects:

πŸ“¦ Projects ⭐ Stars πŸ”€ Forks ℹ️ Issues πŸ“¬ Pull requests
πŸ‘©β€πŸ« Haskell Beginners β€” a Haskell course for complete beginners (no prior knowledge of FP needed!) Stars Forks Issues Pull Requests
🌈 Iris β€” a Haskell CLI framework with the goal to mentor people in OSS and Haskell Stars Forks Issues Pull Requests
✨ zbg β€” Zero Bullshit Git Stars Forks Issues Pull Requests
🧰 tool-sync β€” a CLI tool to download other tools from GitHub Releases Stars Forks Issues Pull Requests
πŸ“Š dr-cabal β€” Haskell dependencies compilation profile Stars Forks Issues Pull Requests
πŸ§ͺ ghc-plugin-non-empty β€” a Haskell compiler plugin for writing type-safe programs easier Stars Forks Issues Pull Requests
πŸ‘ sauron β€” a CLI tool to get top tweets of an account Stars Forks Issues Pull Requests

If you like my work and want to support me, you can sponsor me on GitHub πŸ’– GitHub sponsorship

You can also find me on other social medias:

My toolbox

OCaml Haskell Rust Dhall PostgreSQL Docker Kubernetes Helm Google Cloud Platform git github actions Ubuntu GraphQL

iris's People

Contributors

aleeusgr avatar blackheaven avatar celsobonutti avatar charrsky avatar chshersh avatar cthulehansen avatar dependabot[bot] avatar dponya avatar german1608 avatar himanshu-malviya15 avatar lillycat332 avatar marcellourbani avatar martinhelmer avatar ulysses4ever avatar zetkez avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

iris's Issues

Split errors in Tool check

Currently, the checkTool function has the following type:

checkTool :: cmd -> Tool cmd -> IO ToolCheckResult

where ToolCheckResult is defined as follows:

data ToolCheckResult
    = ToolNotFound Text
    | ToolWrongVersion Text
    | ToolOk

A value of ToolCheckResult is thrown as an exception here:

iris/src/Iris/Env.hs

Lines 138 to 141 in 6be65d4

for_ cliEnvSettingsRequiredTools $ \tool ->
checkTool cmd tool >>= \case
ToolOk -> pure ()
toolErr -> throwIO $ CliEnvException $ CliEnvToolError toolErr

This is suboptimal because the result can never have the ToolOk constructors but we're forcing downstream users to handle this case.

So we want to split success and error cases in the ToolCheckResult type.

Haddock all the things!

Lots of functions are missing the documentation. It would be great to add more!

This issue is an excellent way to start contributing to Iris πŸ€—

See a function that doesn't have a proper explanation? Feel free to add it and it would be especially great if you can add a doctest example! πŸ‘πŸ»

You can document as many functions as you want in a single PR, or you can open multiple PRs. This issue is a call for contributors 😊

The following documentation changes are especially appreciated:

  • Typos, spelling and grammar fixes
  • Adding doctest-like usage examples
  • Explanations on how to use functions and when
  • Documentations to data type fields
  • Docs to function arguments and result types
  • Anything else!

image

Write usage example using Literate Haskell

We would like to have tutorial-like examples in Iris, showcasing project features.

The proposed structure of the example:

  1. Rename app directory to examples
  2. Create a separate directory with an example name for each separate example.
  3. Each example also should have a corresponding executable stanza in the iris.cabal file.
  4. Current example should have name playground
  5. Each example (except playground) should be written using markdown-unlit. The structure of an example:
    • README.md with the example content
    • Main.lhs as a symbolic link to the README

Possible examples:

  • playground
  • Simple grep: search lines containing substring in a given file, print coloured file name to stderr, lines numbers to stderr and with lines to stdout (to mimic the output of ripgrep)

This should be enough for the start but later we can add more examples πŸ™‚

Add CLI options to handle colouring

Currently, Iris checks stdout and stderr handles to decide whether they support colouring or not. And that's all. This issue is about improving the detection of colouring via CLI options.

Description

Following CLI Guidelines, the new logic for detecting colouring should be as follows:

  • Disable colouring if the NO_COLOR / NO_COLOUR environment variable is set
  • Disable colouring if the <MY_APP_NAME>_NO_COLOR / <MY_APP_NAME>_NO_COLOUR environment variable is set
  • Disable colouring if the TERM environment variable is set to dumb
  • Disable colouring if one of --no-color / --no-colour / --disable-color / --disable-colour options is provided
    • Iris should display only --no-color with the description. Other options shouldn't be displayed. Use internal for other options.

Additionally, Iris should provide the --color option (with the --colour option as well being hidden via internal) with the following values:

  • auto (default): detects colouring automatically by checking handles, environment variables and disabling CLI options
  • never: disables colouring

    Colour disabling options should behave exactly as --colour=never

  • always: always prints colours; has higher priority than any other option

Implementation

  • Add a config option for my application name
  • Add new module Iris.Cli.Colour
    • Enum type for always / auto / never options
    • CLI parsers for above-mentioned colours
    • Add this option by default to env
  • Add a new module Iris.Colour.Detect with the implementation of the colouring detection logic described in the beginning of this issue
  • There should be no separate option for stdout and stderr. In other words, env variables and CLI disable / enable options apply to both stdout and stderr. The only different part is whether the specific handle supports colouring or not

Running shell commands

Implement functions to run Shell commands easier in the Iris applications. A simple implementation would be something like this (inspired by shellmet):

-- | Run the command but don't print it
shellSilent
    :: FilePath  -- ^ Executable name
    -> [Text]    -- ^ Arguments
    -> IO ()

-- | Run the command and print the command itself to stderr
shell
    :: Text      -- ^ Prompt
    -> FilePath  -- ^ Executable name
    -> [Text]    -- ^ Arguments
    -> IO ()

-- | Run the command, don't print it and return its stdout
shellRetSilent
    :: FilePath  -- ^ Executable name
    -> [Text]    -- ^ Arguments
    -> IO Text

-- | Run the command, print it with prompt to stderr and return its stdout
shellRet
    :: Text      -- ^ Prompt
    -> FilePath  -- ^ Executable name
    -> [Text]    -- ^ Arguments
    -> IO Text

Alternatively, we can integrate with one of the existing and battle-tested libraries:

Possible usage inside Iris itself β€” Iris.Browse module:

iris/src/Iris/Browse.hs

Lines 69 to 74 in 7e69433

-- | Execute a command with arguments.
runCommand :: FilePath -> [String] -> IO ()
runCommand cmd args = do
let cmdStr = showCommandForUser cmd args
putStrLn $ "βš™ " ++ cmdStr
callCommand cmdStr

Open questions:

  • Would it make sense to run those commands in MonadReader CliEnv to use something specific for running shell commands?

Support stack

Blocked by #38

stack is a common Haskell build tool. Some developers still prefer stack and use only stack for their Haskell development. Adding Stack support would enable contributors who use only stack and allow Iris to be added to Stackage later,

To add stack, one need to:

  1. Add stack.yaml using the latest nightly resolver (so Iris can be added to Stackage later).
  2. Add a CI job for building Iris with stack. Refer to Dead Simple Cross Platform GitHub Actions for Haskell blog post.
  3. Fix all compilation and CI errors.

Configure 'stan'

Stan is a Haskell static analyser. To get the most of Haskell DX, it would be nice to configure it as well.

  • Add Stan config
  • Add Stan CI job
  • Fix or ignore all Stan warnings

Rethink 'CliEnvSettings' and 'defaultCliEnvSettings'

Currently, the CliEnvSettings data type is defined in the following way:

iris/src/Iris/Env.hs

Lines 53 to 71 in 0b9c50d

data CliEnvSettings (cmd :: Type) (appEnv :: Type) = CliEnvSettings
{ -- | @since 0.0.0.0
cliEnvSettingsCmdParser :: Opt.Parser cmd
-- | @since 0.0.0.0
, cliEnvSettingsAppEnv :: appEnv
-- | @since 0.0.0.0
, cliEnvSettingsHeaderDesc :: String
-- | @since 0.0.0.0
, cliEnvSettingsProgDesc :: String
-- | @since 0.0.0.0
, cliEnvSettingsVersionSettings :: Maybe VersionSettings
-- | @since 0.0.0.0
, cliEnvSettingsRequiredTools :: [Tool cmd]
}

And defaultCliEnvSettings:

iris/src/Iris/Env.hs

Lines 78 to 86 in 0b9c50d

defaultCliEnvSettings :: CliEnvSettings () ()
defaultCliEnvSettings = CliEnvSettings
{ cliEnvSettingsCmdParser = pure ()
, cliEnvSettingsAppEnv = ()
, cliEnvSettingsHeaderDesc = "Simple CLI program"
, cliEnvSettingsProgDesc = "CLI tool build with iris - a Haskell CLI framework"
, cliEnvSettingsVersionSettings = Nothing
, cliEnvSettingsRequiredTools = []
}

The idea of using () in the type signature was to rely on type-changing record updates. Unfortunately, the following code doesn't compile:

cmdP :: Parser Cmd
cmdP = ...

appSettings :: Iris.CliEnvSettings Cmd ()
appSettings = Iris.defaultCliEnvSettings
    { Iris.cliEnvSettingsCmdParser = cmdP
    }

It fails with the error:

... location ...
    β€’ Couldn't match type β€˜()’ with β€˜Cmd’
      Expected: CliEnvSettings Cmd ()
        Actual: CliEnvSettings () ()
    β€’ In the expression:
        Iris.defaultCliEnvSettings {cliEnvSettingsCmdParser = cmdP}
      In an equation for β€˜appSettings’:
          appSettings
            = Iris.defaultCliEnvSettings {cliEnvSettingsCmdParser = cmdP}
   |
26 | appSettings = Iris.defaultCliEnvSettings
   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^...

... location ...
    β€’ Couldn't match type β€˜Cmd’ with β€˜()’
      Expected: Options.Applicative.Types.Parser ()
        Actual: Options.Applicative.Types.Parser Cmd
    β€’ In the β€˜cliEnvSettingsCmdParser’ field of a record
      In the expression:
        Iris.defaultCliEnvSettings {cliEnvSettingsCmdParser = cmdP}
      In an equation for β€˜appSettings’:
          appSettings
            = Iris.defaultCliEnvSettings {cliEnvSettingsCmdParser = cmdP}
   |
27 |     { cliEnvSettingsCmdParser = cmdP
   |                                 ^^^^

The problem here because the cliEnvSettingsRequiredTools is also parametrised by cmd. So to change the type, you need to update both fields like this:

appSettings :: Iris.CliEnvSettings Cmd ()
appSettings = Iris.defaultCliEnvSettings
    { Iris.cliEnvSettingsCmdParser = cmdP
    , Iris.cliEnvSettingsRequiredTools = []
    }

Which is a shame. You need to set an extra field you don't care about all the time because you want to change the type of the CLI command.

It would be great to improve this interface. However, I don't have ideas at the moment. This requires some thinking πŸ€”

Fix CONTRIBUTING.md

It seems that this line is unfinished:

ℹ️ NOTE: PRs are merged to the main branch using the "Squash and merge" button. You can produce granular commit history to make the review easier or if it's your

Improve API for detecting required tools on start

Iris.need is the main function for checking whether a specified tool executable in PATH:

need :: MonadIO m => [Tool] -> m ()

It supposed to be used like this in the main application code:

app :: App ()
app = Iris.asksCliEnv Iris.cliEnvCmd >>= \case
    Download url -> do
        need ["curl"]
        runDownload url
    Evaluate hs -> do
        need ["ghc", "cabal"]
        runEvaluate hs

Where the Tool type is the following:

iris/src/Iris/Tool.hs

Lines 43 to 71 in c1597ee

data Tool = Tool
{ toolName :: Text
-- ^ @since 0.0.0.0
, toolSelector :: Maybe ToolSelector
-- ^ @since 0.0.0.0
}
{- |
@since 0.0.0.0
-}
instance IsString Tool where
fromString :: String -> Tool
fromString s =
Tool
{ toolName = fromString s
, toolSelector = Nothing
}
{- |
@since 0.0.0.0
-}
data ToolSelector = ToolSelector
{ toolSelectorFunction :: Text -> Bool
-- ^ @since 0.0.0.0
, toolSelectorVersionArg :: Maybe Text
-- ^ @since 0.0.0.0
}

It does the job but it's a bit suboptimal and maybe inconvenient at times.

I'd love to improve this API in the following way:

  • Provide eDSL for specifying the minimum required version
  • Provide eDSL for specifying the flag for checking the version

With such an eDSL, it should be possible to write the following code:

ghc :: Iris.Tool
ghc = "ghc" `Iris.versionAtLeast` [8, 10, 7] `Iris.usingVersionFlag` "--numeric-version"

cabal :: Iris.Tool
cabal = "cabal" `Iris.versionAtLeast` [3, 6, 2, 0] `Iris.usingVersionFlag` "--numeric-version"

app :: App ()
app = Iris.asksCliEnv Iris.cliEnvCmd >>= \case
    Download url -> do
        need ["curl"]
        runDownload url
    Evaluate hs -> do
        need [ghc, cabal]
        runEvaluate hs

The checkToolFunction should patched accordingly.

Implement Yes/No reading functions

The issue is about creating a type for asking and answering simple interactive questions.

  • Create the module Iris.Interactive.Question.
  • Implement the following type there:
    data YesNo = No | Yes
  • Add the following function to handle various cases like y, Yes, NO, etc. with the following type:
    parseYesNo :: Text -> Maybe YesNo
  • Additionally, create the following function that should ask a question, read the answer and return it
    yesno
        :: (MonadIO m, MonadReader (CliEnv cmd appEnv))
        => Text  -- ^ Question
        -> YesNo  -- ^ Default answer when --no-input is provided
        -> m YesNo  -- ^ Resulting answer
  • Write tests in Test.Iris.Interactive.Question

Usage example:

app :: App ()
app = do
    answer <- Iris.yesno "Would you like to proceed?" Iris.Yes
    case answer of
        Yes -> proceed
        No -> Iris.outLn "Aborting"

Tests for CLI

CLI guidelines suggestion the following convention for named arguments:

  • Every short flag must have long version as well
  • Don’t have ambiguous or similarly-named commands. For example, having two subcommands called β€œupdate” and β€œupgrade” is quite confusing. You might want to use different words, or disambiguate with extra words.
  • The given parser doesn't try to parse pre-defined parsers: --help, --version, --numeric-version, etc.

Is it possible to check such things with optparse-applicative? πŸ€”

Implement Multiple-Choice Reading Option

A generalization for #9 .

The choices can be modeled as their display name coupled with a parsing function to the desired data type of the answer:

data MultipleChoiceOption a = MultipleChoiceOption
    { displayName   :: ByteString  
    , parsingFun    :: ByteString -> Maybe a 
    }

Then, we can model multiple choices as: a message prompt, a non-empty list of choices (whose default choice will always be the head), and a value for the default option:

data MultipleChoicePrompt a = MultipleChoicePrompt
    { promptMessage :: ByteString  
    , promptOptions :: NonEmpty (MultipleChoiceOption a) 
    , defaultValue  :: a  
    }

Thus, the following prompt data:

MultipleChoicePrompt 
    { promptMessage="Are you sure?"
    , promptOptions=
        MultipleChoiceOption 
            { displayName="y"
            , parsingFun = \s -> if s == "y" then Just Yes else Nothing
            }
        :|
        [ MultipleChoiceOption 
            { displayName="n"
            , parsingFun = \s -> if s == "n" then Just No else Nothing
            }
        ]
    , defaultValue=Yes
    }

Will represent:

Are you sure? [y]/n

Finally, we can define a function that:

  • Prints the question (via an aux function that takes the MultipleChoicePrompt
  • Reads answer
  • Returns the first choice that is parsed correctly (can be done with a foldMap over the Alt monoid)
  • Or prints an error message to stderr in case of error, returning Nothing
multipleChoiceQuestion
    ::  MonadIO m
    => MultipleChoicePrompt a
    -> m (Maybe a)

An example run would be:

>>> multipleChoiceQuestion
    MultipleChoicePrompt 
    { promptMessage="Are you sure?"
    , promptOptions=
        MultipleChoiceOption 
            { displayName="y"
            , parsingFun = \s -> if s == "y" then Just Yes else Nothing
            }
        :|
        [ MultipleChoiceOption 
            { displayName="n"
            , parsingFun = \s -> if s == "n" then Just No else Nothing
            }
        , MultipleChoiceOption 
            { displayName="cancel"
            , parsingFun = \s -> if s == "cancel" then Just Cancel else Nothing
            }
        ]
    , defaultValue=Yes
    }
>>> n
Just No

Something to notice is that the function will take the default value and build a parser to accept empty responses:

>>> multipleChoiceQuestion
    MultipleChoicePrompt 
    { promptMessage="Are you sure?"
    , promptOptions=
        MultipleChoiceOption 
            { displayName="y"
            , parsingFun = \s -> if s == "y" then Just Yes else Nothing
            }
        :|
        [ MultipleChoiceOption 
            { displayName="n"
            , parsingFun = \s -> if s == "n" then Just No else Nothing
            }
         , MultipleChoiceOption 
            { displayName="cancel"
            , parsingFun = \s -> if s == "cancel" then Just Cancel else Nothing
            }
        ]
    , defaultValue=Yes
    }
>>> 
Just Yes

Finally, some points that might need refinement:

  • Default value might be different than the value that is returned by the parsing function.
  • Instead of printing to stderr and returning Nothing, it may be better to return Either Bytestring a and let another function decide what to do.
  • Instead of having a Non-Empty List, we could have either a tuple with the first two options and a normal List for the rest, or a sized container (i.e, something from Data.Size). Having a multiple option question with just one option feels wrong.

I'm willing to try and accommodate all suggestions to this implementation :)

Interactive terminal and --no-input

Add a field in the CLI environment, telling if the terminal is interactive.
Also add the --no-input option to disable all interactive actions, e.g. the one from this issue:

Fix all the pre-commit hooks problems

Currently, when running pre-commit hooks, they report multiple problems. It would be nice to fix them all!

βœ—  pre-commit run -a
[INFO] Initializing environment for https://github.com/pre-commit/pre-commit-hooks.
[INFO] Installing environment for https://github.com/pre-commit/pre-commit-hooks.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
Trim Trailing Whitespace.................................................Failed
- hook id: trailing-whitespace
- exit code: 1
- files were modified by this hook

Fixing src/Iris/Cli.hs
Fixing iris.cabal
Fixing src/Iris/Cli/Internal.hs
Fixing src/Iris/Cli/Interactive.hs

Fix End of Files.........................................................Failed
- hook id: end-of-file-fixer
- exit code: 1
- files were modified by this hook

Fixing cabal.project
Fixing .github/CODEOWNERS
Fixing src/Iris/Cli/ParserInfo.hs
Fixing src/Iris/Cli/Internal.hs
Fixing test/Test/Iris/Cli.hs

Check Yaml...............................................................Passed
Check for added large files..............................................Passed

Implement switch parsers

optparse-applicative provides a switch parser with the following type:

switch :: Mod FlagFields Bool -> Parser Bool

It's not entirely clear what does the Bool mean: Is it True when the argument is enabled or when it's default?

I propose to introduce a helper parser in the Iris.Cli.OnOff module:

data OnOff
    = Off
    | On

onoff :: Mod FlagFields Bool -> Parser OnOff

Implement colouring capabilities

The requirements for the design:

  • Simple, convenient and elegant interface
  • Ability to disable colouring
  • Automatic enable/disable colouring support in pure functions as well

I have a sketch of a potential design, we'll get to it once I have time.

Add a way to track the source of configuration parameters

It's extremely useful for diagnostic purposes to understand where a particular setting came from:

  • Default static configuration
  • Configuration file
  • Environment variables
  • CLI options

Prior art:

  • trial: The trial package provides a data structure that allows tracking the source of config and printing this info to terminal

We would like to have this feature in Iris. This ticket is mostly about gathering ideas and designing the implementation.

Any ideas are welcome!

Utility to write files with protection to avoid file override

When using the writeFile function, the program silently overrides the file content. In this case, you may lose your previous content if you're careful enough.

I propose to create a new module Iris.IO and add a protected writeFile function that:

  1. Takes an argument of type: data ActionFlag = Ask | Force
  2. When Ask is given, it checks for file existence and asks a question, whether user wants to override file content.

Configure 'fourmolu'

It would be nice to configure an automatic code formatter, close to the existing style, and a CI check.

I'd like to choose fourmolu because it allows configuration and is still actively developed.

To be specific, the task includes:

  • Choosing a proper config as close as possible to the existing code style
  • Removing .stylish-haskell config and replacing it with the fourmolu config
  • Formatting the entire code
  • Adding a CI check to verify if the code is properly formatted
  • Documenting style guide and how to run a formatter

Write a test that global parsing doesn't conflict with local parsing

Similar to #56

People shouldn't specify options that conflict with ours. But if they still do, it would be nice to know whether their parsing conflicts with ours. So I propose to add two tests and document their results in public API:

  • my-program --no-input when the custom parser specifies --no-input as well (ideally this should be checked by #13)
  • my-program --no-input my-command --no-input is confusing but ideally should be parsed and return different results for different --no-input. If not, having a test is still good and documenting this behaviour is nice.

Add MonadUnliftIO instance

MonadUnliftIO is essential for writing concurrent applications that use custom monads. Since CliApp is essentially a ReaderT, it should be trivial to derive MonadUnliftIO instance.

To do this:

  1. Add unliftio-core to dependencies.
  2. Derive MonadUnliftIO

Add ability to specify Git metadata information

In the --version option, it's often desired to also provide the git commit hash version. The difficulty here is that Haskell packages can be installed from Hackage, not directly from Git, so applications need to somehow embed git metadata.

Current idea is to provide utlitlies for the application to acquire git version in the release stage but not sure about the best way to do this.

Any ideas on how to provide such an utility are appreciated!

Add test for '--help' option parser

It would be nice to test that the --help option by default outputs what we want. And it's also nice to see what changes when we change default parsers.

So I propose to add a test that parses the --help option and compares it with the expected output of this option.

The plan is:

  • Add new module Test.Iris.Cli where the new test will live
  • Extract cmdParserInfo from Iris.Env to Iris.Cli
  • Write a unit test for the --help option using execParserPure from optparse-applicative

Configure 'hlint'

To have a best-in-class dev environment, it would be great to configure HLint for Iris and add a CI job for this.

Specifically:

  • Use the appropriate HLint config and store it in the repo
  • Fix or ignore HLint warnings
  • Add HLint CI job
  • Document hlint usage and requirements

Detect non-interactive terminals automatically

Currently, it's possible to disable interactivity by specifying the --no-input options. But some terminals are non-interactive by nature (e.g. on CI). So it would be great to check this and specify the value of InteractiveMode automatically.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.