packages feed

hwm-0.5.0: src/HWM/CLI/Command/Run.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE NoImplicitPrelude #-}

module HWM.CLI.Command.Run
  ( runScript,
    ScriptOptions,
    TaskCommandOptions,
    InstallCommandOptions,
    runBuild,
    runInstall,
    runTest,
  )
where

import qualified Data.Text as T
import HWM.Core.Common (Name)
import HWM.Core.Parsing (ParseCLI (..), parseOptions)
import HWM.Domain.Build (BuildFlag (..), BuilderCommand (..))
import HWM.Domain.Config (getScript)
import HWM.Domain.ConfigT (ConfigT, config)
import HWM.Domain.Dispatcher (DispatcheCommand (..), dispatchForEach)
import HWM.Domain.Environments (selectEnvironments)
import HWM.Domain.Schema (TargetScope (..))
import HWM.Domain.Workspace (printPkgWSRef, resolveWorkspaces)
import HWM.Runtime.Files (getLocalBinDir, warnBindDir)
import HWM.Runtime.Platform (OS (..), detectPlatform, os)
import HWM.Runtime.Process (Exec (..), inheritRun)
import HWM.Runtime.UI (minRowSize, putLine, sectionWorkspace, uiRow, uiSubPath)
import Options.Applicative
  ( argument,
    help,
    long,
    metavar,
    short,
    str,
  )
import Options.Applicative.Builder (switch)
import Relude

newtype ScriptOptions = ScriptOptions {scriptOptions :: [Text]} deriving (Show)

instance ParseCLI ScriptOptions where
  parseCLI = ScriptOptions <$> many (argument str (metavar "ARGS..." <> help "Arguments to forward to the script"))

runScript :: Name -> ScriptOptions -> ConfigT ()
runScript scriptName ScriptOptions {..} = do
  cfg <- asks config
  script <- getScript scriptName cfg
  platform <- liftIO detectPlatform
  let quoteArg = case os platform of
        Windows -> cmdEscape
        _ -> shEscape
  let scriptCmd = if null scriptOptions then script else script <> " " <> T.unwords (map quoteArg scriptOptions)
  putLine ("❯ " <> scriptCmd)
  inheritRun Exec {execCmd = scriptCmd, execArgs = [], execEnv = [], postCommand = Nothing}
  where
    shEscape :: Text -> Text
    shEscape arg = "'" <> T.replace "'" "'\"'\"'" arg <> "'"

    cmdEscape :: Text -> Text
    cmdEscape arg = "\"" <> T.replace "\"" "\"\"" arg <> "\""

data TaskCommandOptions = TaskCommandOptions
  { opsEnvironments :: [Name],
    opsWorkspaces :: [Name],
    opsFast :: Bool
  }
  deriving (Show)

newtype InstallCommandOptions = InstallCommandOptions {installTaskOptions :: TaskLocalOptions}
  deriving (Show)

data TaskLocalOptions = TaskLocalOptions
  { localWorkspaces :: [Name],
    localFast :: Bool
  }
  deriving (Show)

instance ParseCLI TaskCommandOptions where
  parseCLI =
    TaskCommandOptions
      <$> parseOptions (long "env" <> short 'e' <> metavar "ENV" <> help "Run in specific env (use 'all' for full matrix)")
      <*> many (argument str (metavar "WORKSPACE" <> help "Limit to package (core) or group (libs)"))
      <*> switch (long "fast" <> help "Enable fast mode")

instance ParseCLI InstallCommandOptions where
  parseCLI =
    InstallCommandOptions
      <$> ( TaskLocalOptions
              <$> many (argument str (metavar "WORKSPACE" <> help "Limit to package (core) or group (libs)"))
              <*> switch (long "fast" <> help "Enable fast mode")
          )

parseTargets :: [Name] -> ConfigT TargetScope
parseTargets names = case names of
  [] -> do
    sectionWorkspace $ uiRow minRowSize "scope" "Global"
    pure ScopeGlobal
  _ -> do
    pkgs <- concatMap snd <$> resolveWorkspaces names
    sectionWorkspace $ for_ pkgs $ \pkg -> uiSubPath (printPkgWSRef pkg)
    pure $ ScopePkgs pkgs

runBuild :: TaskCommandOptions -> ConfigT ()
runBuild TaskCommandOptions {..} = do
  scope <- parseTargets opsWorkspaces
  envs <- selectEnvironments opsEnvironments
  dispatchForEach (DispatcheCommand Build scope [BuildFastFlag | opsFast]) envs

runInstall :: InstallCommandOptions -> ConfigT ()
runInstall (InstallCommandOptions TaskLocalOptions {..}) = do
  scope <- parseTargets localWorkspaces
  envs <- selectEnvironments []
  binDir <- getLocalBinDir
  warnBindDir binDir
  dispatchForEach (DispatcheCommand (Install binDir) scope [BuildFastFlag | localFast]) envs

runTest :: TaskCommandOptions -> ConfigT ()
runTest TaskCommandOptions {..} = do
  scope <- parseTargets opsWorkspaces
  envs <- selectEnvironments opsEnvironments
  dispatchForEach (DispatcheCommand Test scope [BuildFastFlag | opsFast]) envs