scripts/stricter/stricter.hs (92 lines of code) (raw):

#!/usr/bin/env stack {- Copyright (c) Meta Platforms, Inc. and affiliates. This source code is licensed under the MIT license found in the LICENSE file in the root directory of this source tree. -} -- stack --resolver lts-14.3 --install-ghc runghc --package turtle --package system-filepath --package foldl --package typed-process --package bytestring {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE FlexibleContexts #-} import Prelude hiding (FilePath) import Turtle import Data.Maybe (catMaybes) import Control.Monad (forM_) import Data.List ((\\)) import qualified Filesystem.Path.CurrentOS as Path import qualified System.Process.Typed as Proc import qualified Data.Text as T import qualified Control.Foldl as F import qualified Data.ByteString.Char8 as C import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as BSL -- * Global settings flipperPath :: FilePath -> FilePath flipperPath basePath = basePath </> "xplat" </> "sonar" -- * Application logic -- | Find the root of the project, indicated by the presence of a ".hg" folder. findProjectRoot :: FilePath -> IO (Maybe FilePath) findProjectRoot dir = go $ Path.splitDirectories dir where go :: forall (m :: * -> *). MonadIO m => [FilePath] -> m (Maybe FilePath) go [] = return Nothing go ds = do let ds' = init ds dir' = Path.concat ds' hg = dir' </> ".hg" hgExists <- testdir hg if hgExists then return $ Just dir' else go ds' data TSCResult = TSCResult { numErrors :: Int , errors :: [BS.ByteString] } deriving (Show, Eq) runTSC :: FilePath -> Shell TSCResult runTSC root = do cd root (exitCode, stdout, stderr) <- liftIO $ Proc.readProcess (Proc.proc "yarn" ["run", "tsc", "--strict"]) let errors = C.split '\n' (BSL.toStrict stdout) & filter (BS.isInfixOf ": error TS") pure $ TSCResult { numErrors = length errors , errors = errors } hgPrev :: Shell () hgPrev = procs "hg" ["prev"] mempty hgNext :: Shell () hgNext = procs "hg" ["next"] mempty handleErr :: IO ExitCode handleErr = err "Failed to run hg/tsc. Check above output." >> (pure $ ExitFailure 2) handleRes :: TSCResult -> TSCResult -> IO ExitCode handleRes cur prev = do let delta = numErrors cur - numErrors prev if delta > 0 then do eprintf ("TSC Strict Mode regression. "%d%" new violations introduced:\n") delta forM_ (errors cur \\ errors prev) $ eprintf ("- "%w%"\n") eprintf "Please visit https://fburl.com/strictflipper for more information.\n" return $ ExitFailure 1 else do printf ("TSC Strict Mode test passed. Delta: "%d%"\n") delta return ExitSuccess main :: IO () main = do projectRoot <- findProjectRoot =<< pwd let flipperDir = flipperPath <$> projectRoot flipperDir_ <- case flipperDir of Just f -> realpath f Nothing -> die "Couldn't determine Flipper project location." printf "Running tsc --strict against current revision.\n" currentRes <- fold (runTSC flipperDir_) F.head printf "Checking out hg prev.\n" _ <- sh hgPrev printf "Running tsc --strict against previous revision.\n" prevRes <- fold (runTSC flipperDir_) F.head printf "Checking out hg next.\n" _ <- sh hgNext exit =<< case (currentRes, prevRes) of (Just cur, Just prev) -> handleRes cur prev _ -> handleErr