From 6664d5e934804838f6d598eadc40ecdd328d3f77 Mon Sep 17 00:00:00 2001 From: Colin Wahl Date: Wed, 9 Sep 2020 17:48:02 -0700 Subject: [PATCH 1/6] add github-actions-toolkit as a dependency --- packages.dhall | 18 ++++++++++++++++++ spago.dhall | 1 + 2 files changed, 19 insertions(+) diff --git a/packages.dhall b/packages.dhall index fd3b610..14d3080 100644 --- a/packages.dhall +++ b/packages.dhall @@ -23,3 +23,21 @@ in upstream , version = "v5.0.1" } + + with github-actions-toolkit = + { dependencies = + [ "aff" + , "aff-promise" + , "effect" + , "foreign-object" + , "node-buffer" + , "node-path" + , "node-streams" + , "nullable" + , "transformers" + ] + , repo = + "https://github.com/purescript-contrib/purescript-github-actions-toolkit" + , version = + "v0.1.0" + } diff --git a/spago.dhall b/spago.dhall index 1f69963..883400e 100644 --- a/spago.dhall +++ b/spago.dhall @@ -8,6 +8,7 @@ , "argonaut-core" , "console" , "effect" + , "github-actions-toolkit" , "node-fs" , "node-path" , "node-process" From 13687f5f8e36c9e72ab4ee2f369992ec5664473e Mon Sep 17 00:00:00 2001 From: Colin Wahl Date: Wed, 9 Sep 2020 17:49:38 -0700 Subject: [PATCH 2/6] remove src/Actions bindings --- src/Actions/Core.js | 22 --------- src/Actions/Core.purs | 47 ------------------- src/Actions/Exec.js | 3 -- src/Actions/Exec.purs | 20 -------- src/Actions/ToolCache.js | 15 ------ src/Actions/ToolCache.purs | 95 -------------------------------------- 6 files changed, 202 deletions(-) delete mode 100644 src/Actions/Core.js delete mode 100644 src/Actions/Core.purs delete mode 100644 src/Actions/Exec.js delete mode 100644 src/Actions/Exec.purs delete mode 100644 src/Actions/ToolCache.js delete mode 100644 src/Actions/ToolCache.purs diff --git a/src/Actions/Core.js b/src/Actions/Core.js deleted file mode 100644 index dc811cc..0000000 --- a/src/Actions/Core.js +++ /dev/null @@ -1,22 +0,0 @@ -const core = require("@actions/core"); - -exports.addPath = (path) => () => core.addPath(path); - -exports.debug = (msg) => () => core.debug(msg); - -exports.error = (msg) => () => core.error(msg); - -exports.exportVariable = ({ key, value }) => () => - core.exportVariable(key, value); - -exports.getInputImpl = (name) => () => { - const input = core.getInput(name); - if (input === "") return null; - return input; -}; - -exports.info = (msg) => () => core.info(msg); - -exports.setFailed = (msg) => () => core.setFailed(msg); - -exports.warning = (msg) => () => core.warning(msg); diff --git a/src/Actions/Core.purs b/src/Actions/Core.purs deleted file mode 100644 index b158b64..0000000 --- a/src/Actions/Core.purs +++ /dev/null @@ -1,47 +0,0 @@ --- | Exports functions from the @actions/core module provided by GitHub --- | https://github.com/actions/toolkit/tree/main/packages/core -module Actions.Core - ( addPath - , debug - , error - , exportVariable - , getInput - , info - , setFailed - , warning - ) where - -import Prelude - -import Data.Maybe (Maybe) -import Data.Nullable (Nullable, toMaybe) -import Effect (Effect) -import Setup.Data.Key (Key) - --- | Prepends input path to the PATH (for this action and future actions) -foreign import addPath :: String -> Effect Unit - --- | Writes debug message to user log -foreign import debug :: String -> Effect Unit - --- | Writes error message to user log -foreign import error :: String -> Effect Unit - --- | Sets env variable for this action and future actions in the job -foreign import exportVariable :: { key :: String, value :: String } -> Effect Unit - -foreign import getInputImpl :: Key -> Effect (Nullable String) - --- | Gets the value of an input. The value is also trimmed. -getInput :: Key -> Effect (Maybe String) -getInput = map toMaybe <<< getInputImpl - --- | Writes info message to user log -foreign import info :: String -> Effect Unit - --- | Sets the action status to failed. When the action exits it will be with an --- | exit code of 1 -foreign import setFailed :: String -> Effect Unit - --- | Writes warning message to user log -foreign import warning :: String -> Effect Unit diff --git a/src/Actions/Exec.js b/src/Actions/Exec.js deleted file mode 100644 index 1a509c0..0000000 --- a/src/Actions/Exec.js +++ /dev/null @@ -1,3 +0,0 @@ -const exec = require("@actions/exec"); - -exports.execImpl = exec.exec; diff --git a/src/Actions/Exec.purs b/src/Actions/Exec.purs deleted file mode 100644 index 7dbd693..0000000 --- a/src/Actions/Exec.purs +++ /dev/null @@ -1,20 +0,0 @@ --- | Exports functions from the @actions/exec module provided by GitHub --- | https://github.com/actions/toolkit/tree/main/packages/exec -module Actions.Exec - ( exec - ) where - -import Prelude - -import Control.Promise (Promise, toAffE) -import Effect.Aff (Aff) -import Effect.Uncurried (EffectFn2, runEffectFn2) - -foreign import execImpl :: EffectFn2 String (Array String) (Promise Number) - --- | Executes a command on the command line, with arguments -exec :: String -> Array String -> Aff { succeeded :: Boolean } -exec command args = - map ((_ == 0.0) >>> { succeeded: _ }) - $ toAffE - $ runEffectFn2 execImpl command args diff --git a/src/Actions/ToolCache.js b/src/Actions/ToolCache.js deleted file mode 100644 index 5464492..0000000 --- a/src/Actions/ToolCache.js +++ /dev/null @@ -1,15 +0,0 @@ -const tc = require("@actions/tool-cache"); - -exports.cacheDirImpl = tc.cacheDir; - -exports.cacheFileImpl = tc.cacheFile; - -exports.downloadToolImpl = tc.downloadTool; - -exports.extractTarImpl = tc.extractTar; - -// We manually make this function an effect, as it doesn't return a Promise and -// can throw exceptions. -exports.findImpl = (toolName) => (versionSpec) => () => { - return tc.find(toolName, versionSpec); -}; diff --git a/src/Actions/ToolCache.purs b/src/Actions/ToolCache.purs deleted file mode 100644 index 5a571ae..0000000 --- a/src/Actions/ToolCache.purs +++ /dev/null @@ -1,95 +0,0 @@ --- | Exports functions from the @actions/tool-cache module provided by GitHub --- | https://github.com/actions/toolkit/tree/main/packages/tool-cache -module Actions.ToolCache - ( CacheOptions - , cacheDir - , cacheFile - , downloadTool - , downloadTool' - , extractTar - , extractTar' - , find - ) where - -import Prelude - -import Control.Promise (Promise, toAffE) -import Data.Maybe (Maybe(..)) -import Data.Nullable (Nullable, notNull, null, toMaybe) -import Data.String as String -import Data.Version (Version) -import Data.Version as Version -import Effect (Effect) -import Effect.Aff (Aff) -import Effect.Uncurried (EffectFn2, EffectFn3, EffectFn4, runEffectFn2, runEffectFn3, runEffectFn4) -import Node.Path (FilePath) -import Setup.Data.Tool (Tool) -import Setup.Data.Tool as Tool - -type ToolName = String - -type CacheOptions = - { source :: FilePath - , tool :: Tool - , version :: Version - } - -foreign import cacheDirImpl :: EffectFn3 FilePath ToolName String (Promise FilePath) - --- | Caches a directory and installs it into the tool cacheDir -cacheDir :: CacheOptions -> Aff FilePath -cacheDir { source, tool, version } = do - let - toolName = Tool.name tool - version' = Version.showVersion version - - toAffE (runEffectFn3 cacheDirImpl source toolName version') - -foreign import cacheFileImpl :: EffectFn4 FilePath String ToolName String (Promise FilePath) - --- | Caches a downloaded file (GUID) and installs it into the tool cache -cacheFile :: CacheOptions -> Aff FilePath -cacheFile { source, tool, version } = do - let - toolName = Tool.name tool - version' = Version.showVersion version - - -- We use the same name for the `targetName` and the `toolName` as the tool - -- name is the executable name. - toAffE (runEffectFn4 cacheFileImpl source toolName toolName version') - -foreign import downloadToolImpl :: EffectFn2 String (Nullable String) (Promise FilePath) - --- | Download a tool from a URL and stream it into a file, returning the file path -downloadTool' :: String -> Aff FilePath -downloadTool' url = toAffE (runEffectFn2 downloadToolImpl url null) - --- | Download a tool from an url and stream it into the destination file path, --- | returning the file path -downloadTool :: String -> FilePath -> Aff FilePath -downloadTool url dest = toAffE (runEffectFn2 downloadToolImpl url (notNull dest)) - -foreign import extractTarImpl :: EffectFn2 FilePath (Nullable FilePath) (Promise FilePath) - --- | Extract a compressed tar archive, returning the resulting file path -extractTar' :: FilePath -> Aff FilePath -extractTar' src = toAffE (runEffectFn2 extractTarImpl src null) - --- | Extract a compressed tar archive to a target destination, returning the --- | resulting file path -extractTar :: FilePath -> FilePath -> Aff FilePath -extractTar src dest = toAffE (runEffectFn2 extractTarImpl src (notNull dest)) - -foreign import findImpl :: ToolName -> String -> Effect (Nullable FilePath) - --- | Finds the path to a tool version in the local installed tool cache -find :: Tool -> Version -> Effect (Maybe FilePath) -find tool version = do - let - toolName = Tool.name tool - toolVersion = Version.showVersion version - checkNull fp - | String.null fp = Nothing - | otherwise = Just fp - - map (checkNull <=< toMaybe) (findImpl toolName toolVersion) From 5876b678c99173d931ead0241f20b05670b95ef8 Mon Sep 17 00:00:00 2001 From: Colin Wahl Date: Wed, 9 Sep 2020 20:29:38 -0700 Subject: [PATCH 3/6] implement library with github-actions-toolkit library bindings --- src/Main.purs | 17 ++++++++++---- src/Setup/BuildPlan.purs | 48 ++++++++++++++++++---------------------- src/Setup/Data/Key.purs | 3 +++ src/Setup/GetTool.purs | 24 +++++++++++--------- 4 files changed, 51 insertions(+), 41 deletions(-) diff --git a/src/Main.purs b/src/Main.purs index cfbacd1..da5a320 100644 --- a/src/Main.purs +++ b/src/Main.purs @@ -2,18 +2,27 @@ module Main where import Prelude +import Control.Monad.Except.Trans (ExceptT(..), runExceptT) import Data.Argonaut.Core (Json) +import Data.Either (Either(..)) import Data.Foldable (traverse_) import Effect (Effect) -import Effect.Aff (launchAff_) +import Effect.Aff (launchAff_, runAff_) +import Effect.Class (liftEffect) +import Effect.Exception (message) +import GitHub.Actions.Core as Core import Setup.BuildPlan (constructBuildPlan) import Setup.GetTool (getTool) import Setup.UpdateVersions (updateVersions) main :: Json -> Effect Unit -main json = do - tools <- constructBuildPlan json - launchAff_ $ traverse_ getTool tools +main json = runAff_ go $ runExceptT do + tools <- ExceptT (liftEffect (runExceptT (constructBuildPlan json))) + traverse_ getTool tools + where + go res = case join res of + Left err -> Core.setFailed (message err) + Right a -> pure unit update :: Effect Unit update = launchAff_ updateVersions diff --git a/src/Setup/BuildPlan.purs b/src/Setup/BuildPlan.purs index f903d93..202731f 100644 --- a/src/Setup/BuildPlan.purs +++ b/src/Setup/BuildPlan.purs @@ -2,22 +2,24 @@ module Setup.BuildPlan (constructBuildPlan, BuildPlan) where import Prelude -import Actions.Core as Core +import Control.Monad.Except.Trans (ExceptT) import Data.Argonaut.Core (Json) import Data.Argonaut.Decode (decodeJson, printJsonDecodeError, (.:)) -import Data.Array as Array -import Data.Bifunctor (bimap, lmap) +import Data.Bifunctor (lmap) import Data.Either (Either(..)) import Data.Foldable (fold) -import Data.Maybe (Maybe(..)) +import Data.Newtype (unwrap) import Data.Traversable (traverse) import Data.Version (Version) import Data.Version as Version import Effect (Effect) import Effect.Aff (error, throwError) +import Effect.Class (liftEffect) +import Effect.Exception (Error) +import GitHub.Actions.Core as Core import Setup.Data.Key (Key) import Setup.Data.Key as Key -import Setup.Data.Tool (Tool, required) +import Setup.Data.Tool (Tool) import Setup.Data.Tool as Tool import Text.Parsing.Parser (parseErrorMessage) import Text.Parsing.Parser as ParseError @@ -26,42 +28,34 @@ import Text.Parsing.Parser as ParseError type BuildPlan = Array { tool :: Tool, version :: Version } -- | Construct the list of tools that sholud be downloaded and cached by the action -constructBuildPlan :: Json -> Effect BuildPlan -constructBuildPlan json = map Array.catMaybes $ traverse (resolve json) Tool.allTools +constructBuildPlan :: Json -> ExceptT Error Effect BuildPlan +constructBuildPlan json = traverse (resolve json) Tool.allTools -- | The parsed value of an input field that specifies a version data VersionField = Latest | Exact Version -- | Attempt to read the value of an input specifying a tool version -getVersionField :: Key -> Effect (Maybe (Either String VersionField)) -getVersionField = map (map parse) <<< Core.getInput - where - parse = case _ of +getVersionField :: Key -> ExceptT Error Effect VersionField +getVersionField key = do + value <- Core.getInput' (unwrap key) + case value of "latest" -> pure Latest - value -> bimap ParseError.parseErrorMessage Exact (Version.parseVersion value) + val -> case Version.parseVersion val of + Left msg -> throwError (error (ParseError.parseErrorMessage msg)) + Right version -> pure (Exact version) -- | Resolve the exact version to provide for a tool in the environment, based -- | on the action.yml file. -resolve :: Json -> Tool -> Effect (Maybe { tool :: Tool, version :: Version }) +resolve :: Json -> Tool -> ExceptT Error Effect { tool :: Tool, version :: Version } resolve versionsContents tool = do let key = Key.fromTool tool - getVersionField key >>= case _ of - Nothing | required tool -> throwError $ error "No input received for required key." - Nothing -> pure Nothing - Just field -> map Just $ getVersion field - - where - getVersion :: Either String VersionField -> Effect { tool :: Tool, version :: Version } - getVersion = case _ of - Left err -> do - Core.setFailed $ fold [ "Unable to parse version: ", err ] - throwError $ error "Unable to complete fetching version." - - Right (Exact v) -> do + field <- getVersionField key + case field of + Exact v -> liftEffect do Core.info "Found exact version" pure { tool, version: v } - Right Latest -> do + Latest -> liftEffect do Core.info $ fold [ "Fetching latest tag for ", Tool.name tool ] let diff --git a/src/Setup/Data/Key.purs b/src/Setup/Data/Key.purs index bbc3fb7..bde02b6 100644 --- a/src/Setup/Data/Key.purs +++ b/src/Setup/Data/Key.purs @@ -3,10 +3,13 @@ module Setup.Data.Key , fromTool ) where +import Data.Newtype (class Newtype) import Setup.Data.Tool (Tool(..)) newtype Key = Key String +derive instance newtypeKey :: Newtype Key _ + purescriptKey :: Key purescriptKey = Key "purescript" diff --git a/src/Setup/GetTool.purs b/src/Setup/GetTool.purs index 824a18a..a7612b8 100644 --- a/src/Setup/GetTool.purs +++ b/src/Setup/GetTool.purs @@ -2,19 +2,22 @@ module Setup.GetTool (getTool) where import Prelude -import Actions.Core as Core -import Actions.Exec as Exec -import Actions.ToolCache as ToolCache +import Control.Monad.Except.Trans (ExceptT(..), runExceptT) import Data.Foldable (fold) import Data.Maybe (Maybe(..)) import Data.Version (Version) +import Data.Version as Version import Effect.Aff (Aff) import Effect.Class (liftEffect) +import Effect.Exception (Error) +import GitHub.Actions.Core as Core +import GitHub.Actions.Exec as Exec +import GitHub.Actions.ToolCache as ToolCache import Setup.Data.Platform (Platform(..), platform) import Setup.Data.Tool (InstallMethod(..), Tool) import Setup.Data.Tool as Tool -getTool :: { tool :: Tool, version :: Version } -> Aff Unit +getTool :: { tool :: Tool, version :: Version } -> ExceptT Error Aff Unit getTool { tool, version } = do let name = Tool.name tool @@ -22,17 +25,18 @@ getTool { tool, version } = do case installMethod of Tarball opts -> do - liftEffect (ToolCache.find tool version) >>= case _ of + let + hoist = runExceptT >>> liftEffect >>> ExceptT + mbPath <- hoist $ ToolCache.find { arch: Nothing, toolName: name, versionSpec: Version.showVersion version } + case mbPath of Just path -> liftEffect do Core.info $ fold [ "Found cached version of ", name ] Core.addPath path Nothing -> do - liftEffect $ Core.info $ fold [ "Did not find cached version of ", name ] - downloadPath <- ToolCache.downloadTool' opts.source extractedPath <- ToolCache.extractTar' downloadPath - cached <- ToolCache.cacheFile { source: opts.getExecutablePath extractedPath, tool, version } + cached <- ToolCache.cacheFile { sourceFile: opts.getExecutablePath extractedPath, tool: name, version: Version.showVersion version, targetFile: name, arch: Nothing } liftEffect do Core.info $ fold [ "Cached path ", cached, ", adding to PATH" ] @@ -40,6 +44,6 @@ getTool { tool, version } = do NPM package -> void $ case platform of Windows -> - Exec.exec "npm" [ "install", "-g", package ] + Exec.exec { command: "npm", args: Just [ "install", "-g", package ], options: Nothing } _ -> - Exec.exec "sudo npm" [ "install", "-g", package ] + Exec.exec { command: "sudo npm", args: Just [ "install", "-g", package ], options: Nothing } From 62a3a546c94fb22198ced29edf0e46b4b48f963f Mon Sep 17 00:00:00 2001 From: Colin Wahl Date: Wed, 9 Sep 2020 22:23:54 -0700 Subject: [PATCH 4/6] mapExceptT liftEffect --- src/Main.purs | 4 ++-- src/Setup/GetTool.purs | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Main.purs b/src/Main.purs index da5a320..29654d0 100644 --- a/src/Main.purs +++ b/src/Main.purs @@ -2,7 +2,7 @@ module Main where import Prelude -import Control.Monad.Except.Trans (ExceptT(..), runExceptT) +import Control.Monad.Except.Trans (mapExceptT, runExceptT) import Data.Argonaut.Core (Json) import Data.Either (Either(..)) import Data.Foldable (traverse_) @@ -17,7 +17,7 @@ import Setup.UpdateVersions (updateVersions) main :: Json -> Effect Unit main json = runAff_ go $ runExceptT do - tools <- ExceptT (liftEffect (runExceptT (constructBuildPlan json))) + tools <- mapExceptT liftEffect $ constructBuildPlan json traverse_ getTool tools where go res = case join res of diff --git a/src/Setup/GetTool.purs b/src/Setup/GetTool.purs index a7612b8..a5c2af0 100644 --- a/src/Setup/GetTool.purs +++ b/src/Setup/GetTool.purs @@ -2,7 +2,7 @@ module Setup.GetTool (getTool) where import Prelude -import Control.Monad.Except.Trans (ExceptT(..), runExceptT) +import Control.Monad.Except.Trans (ExceptT, mapExceptT) import Data.Foldable (fold) import Data.Maybe (Maybe(..)) import Data.Version (Version) @@ -25,9 +25,7 @@ getTool { tool, version } = do case installMethod of Tarball opts -> do - let - hoist = runExceptT >>> liftEffect >>> ExceptT - mbPath <- hoist $ ToolCache.find { arch: Nothing, toolName: name, versionSpec: Version.showVersion version } + mbPath <- mapExceptT liftEffect $ ToolCache.find { arch: Nothing, toolName: name, versionSpec: Version.showVersion version } case mbPath of Just path -> liftEffect do Core.info $ fold [ "Found cached version of ", name ] From 453fca8bf7760ee0c6077c76bd56b1e44e0a8fe8 Mon Sep 17 00:00:00 2001 From: Colin Wahl Date: Wed, 9 Sep 2020 22:25:30 -0700 Subject: [PATCH 5/6] run spago upgrade-set and remove github-actions-toolkit override --- packages.dhall | 54 ++++++++++++++++---------------------------------- 1 file changed, 17 insertions(+), 37 deletions(-) diff --git a/packages.dhall b/packages.dhall index 14d3080..25c51de 100644 --- a/packages.dhall +++ b/packages.dhall @@ -1,43 +1,23 @@ let upstream = - https://github.com/purescript/package-sets/releases/download/psc-0.13.8-20200724/packages.dhall sha256:bb941d30820a49345a0e88937094d2b9983d939c9fd3a46969b85ce44953d7d9 + https://github.com/purescript/package-sets/releases/download/psc-0.13.8-20200909/packages.dhall sha256:b899488adf6f02a92bbaae88039935bbc61bcba4cf4462f6d915fc3d0e094604 in upstream with versions = { dependencies = - [ "console" - , "control" - , "either" - , "exceptions" - , "foldable-traversable" - , "functions" - , "integers" - , "lists" - , "maybe" - , "orders" - , "parsing" - , "partial" - , "strings" - ] - , repo = - "https://github.com/hdgarrood/purescript-versions.git" - , version = - "v5.0.1" - } - - with github-actions-toolkit = - { dependencies = - [ "aff" - , "aff-promise" - , "effect" - , "foreign-object" - , "node-buffer" - , "node-path" - , "node-streams" - , "nullable" - , "transformers" - ] - , repo = - "https://github.com/purescript-contrib/purescript-github-actions-toolkit" - , version = - "v0.1.0" + [ "console" + , "control" + , "either" + , "exceptions" + , "foldable-traversable" + , "functions" + , "integers" + , "lists" + , "maybe" + , "orders" + , "parsing" + , "partial" + , "strings" + ] + , repo = "https://github.com/hdgarrood/purescript-versions.git" + , version = "v5.0.1" } From 2c957c4a3f6e79f6d2e717dc75985675bce310c3 Mon Sep 17 00:00:00 2001 From: Colin Wahl Date: Wed, 9 Sep 2020 22:29:50 -0700 Subject: [PATCH 6/6] remove random changes to packages.dhall --- packages.dhall | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/packages.dhall b/packages.dhall index 25c51de..ace592b 100644 --- a/packages.dhall +++ b/packages.dhall @@ -4,20 +4,22 @@ let upstream = in upstream with versions = { dependencies = - [ "console" - , "control" - , "either" - , "exceptions" - , "foldable-traversable" - , "functions" - , "integers" - , "lists" - , "maybe" - , "orders" - , "parsing" - , "partial" - , "strings" - ] - , repo = "https://github.com/hdgarrood/purescript-versions.git" - , version = "v5.0.1" + [ "console" + , "control" + , "either" + , "exceptions" + , "foldable-traversable" + , "functions" + , "integers" + , "lists" + , "maybe" + , "orders" + , "parsing" + , "partial" + , "strings" + ] + , repo = + "https://github.com/hdgarrood/purescript-versions.git" + , version = + "v5.0.1" }