Skip to content
Draft
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

Features:
* Reinstate `spago script` for running standalone PureScript files with package-set and dependency options

Bugfixes:
* Fix flaky `SQLITE_IOERR_TRUNCATE` on Windows when multiple spago processes connect concurrently to the cache DB, by skipping `PRAGMA journal_mode = WAL` when it's already enabled (WAL mode is persistent in the DB file header) and tolerating the race on the initial set
* Retry transient network failures (connection errors and 5xx responses) when fetching package tarballs and calling the registry API, instead of failing immediately
Expand Down
17 changes: 17 additions & 0 deletions bin/src/Flags.purs
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,23 @@ packages =
<> O.help "Package name to add as dependency"
)

scriptSource :: Parser String
scriptSource =
O.strArgument
( O.metavar "SOURCE"
<> O.help "PureScript source file to run"
)

scriptDependencies :: Parser (List String)
scriptDependencies =
O.many $
O.strOption
( O.long "dependency"
<> O.short 'd'
<> O.metavar "PACKAGE"
<> O.help "Package dependency to make available to the script"
)

packagesToRemove :: Parser (List String)
packagesToRemove =
O.many $
Expand Down
83 changes: 79 additions & 4 deletions bin/src/Main.purs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import Spago.Command.Registry (RegistryInfoArgs, RegistryPackageSetsArgs, Regist
import Spago.Command.Registry as RegistryCmd
import Spago.Command.Repl as Repl
import Spago.Command.Run as Run
import Spago.Command.Script as Script
import Spago.Command.Sources as Sources
import Spago.Command.Test as Test
import Spago.Command.Uninstall as Uninstall
Expand Down Expand Up @@ -145,6 +146,12 @@ type RunArgs =
, pure :: Boolean
}

type ScriptArgs =
{ source :: String
, dependencies :: List String
, packageSet :: Maybe String
}

type TestArgs =
{ selectedPackage :: Maybe String
, output :: Maybe String
Expand Down Expand Up @@ -212,6 +219,7 @@ data Command a
| RegistryTransfer RegistryTransferArgs
| Repl ReplArgs
| Run RunArgs
| Script ScriptArgs
| Sources SourcesArgs
| Test TestArgs
| Upgrade UpgradeArgs
Expand Down Expand Up @@ -270,6 +278,7 @@ argParser =
)
, commandParser "repl" (Repl <$> replArgsParser) "Start a REPL"
, commandParser "run" (Run <$> runArgsParser) "Run the project"
, commandParser "script" (Script <$> scriptArgsParser) "Run a standalone PureScript source file"
, commandParser "sources" (Sources <$> sourcesArgsParser) "List all the source paths (globs) for the dependencies of the project"
, commandParser "test" (Test <$> testArgsParser) "Test the project"
, commandParser "uninstall" (Uninstall <$> uninstallArgsParser) "Remove dependencies from a package"
Expand Down Expand Up @@ -414,6 +423,14 @@ bundleArgsParser =
, pure: Flags.pureLockfile
}

scriptArgsParser :: Parser ScriptArgs
scriptArgsParser =
Optparse.fromRecord
{ source: Flags.scriptSource
, dependencies: Flags.scriptDependencies
, packageSet: Flags.maybeSetVersion
}

publishArgsParser :: Parser PublishArgs
publishArgsParser =
Optparse.fromRecord
Expand Down Expand Up @@ -657,8 +674,66 @@ main = do
let options = { depsOnly: false, pursArgs: List.toUnfoldable args.pursArgs, jsonErrors: false }
built <- runSpago buildEnv (Build.run options)
when built do
runEnv <- runSpago env (mkRunEnv args buildEnv)
runEnv <- runSpago env (mkRunEnv args buildEnv Nothing)
runSpago runEnv Run.run
Script args -> do
originalCwd <- Paths.cwd
sourcePath <- Path.toAbsolute (Path.global args.source)
tmpDir <- mkTemp
FS.mkdirp tmpDir
Paths.chdir tmpDir
tmpRootPath <- Path.mkRoot tmpDir
registryEnv <- mkRegistryEnv offline <#> Record.union { rootPath: tmpRootPath }
setVersion <- parseSetVersion args.packageSet
void $ runSpago registryEnv $ Init.run
{ setVersion
, mode: Init.InitWorkspace { packageName: Just "script" }
, useSolver: false
}
FS.copyTree
{ src: sourcePath
, dst: tmpRootPath </> "src" </> (Script.moduleName <> ".purs")
}
{ env, fetchOpts } <- mkFetchEnv
{ packages: args.dependencies
, selectedPackage: Nothing
, ensureRanges: false
, testDeps: false
, isRepl: false
, pure: false
, migrateConfig: false
, offline
}
dependencies <- runSpago env (Fetch.run fetchOpts)
buildEnv <- runSpago env
( mkBuildEnv
{ backendArgs: mempty
, output: Nothing
, pedanticPackages: false
, statVerbosity: Nothing
, strict: Nothing
}
dependencies
)
built <- runSpago buildEnv (Build.run { depsOnly: false, pursArgs: mempty, jsonErrors: false })
when built do
let
runArgs =
{ selectedPackage: Nothing
, output: Nothing
, pedanticPackages: false
, pursArgs: mempty
, backendArgs: mempty
, execArgs: Nothing
, main: Just Script.moduleName
, ensureRanges: false
, strict: Nothing
, statVerbosity: Nothing
, pure: false
}
runEnv <- runSpago env (mkRunEnv runArgs buildEnv (Just $ Path.toGlobal originalCwd))
runSpago runEnv Run.run
Paths.chdir originalCwd
Test args@{ selectedPackage, pure } -> do
{ env, fetchOpts } <- mkFetchEnv { packages: mempty, selectedPackage, pure, ensureRanges: false, testDeps: false, isRepl: false, migrateConfig, offline }
dependencies <- runSpago env (Fetch.run fetchOpts)
Expand Down Expand Up @@ -787,8 +862,8 @@ mkBundleEnv bundleArgs { dependencies, purs } = do
let bundleEnv = { esbuild, logOptions, rootPath, workspace: newWorkspace, selected, bundleOptions, purs, dependencies }
pure bundleEnv

mkRunEnv :: forall a b. RunArgs -> Build.BuildEnv b -> Spago (Fetch.FetchEnv a) (Run.RunEnv ())
mkRunEnv runArgs { dependencies, purs } = do
mkRunEnv :: forall a b. RunArgs -> Build.BuildEnv b -> Maybe GlobalPath -> Spago (Fetch.FetchEnv a) (Run.RunEnv ())
mkRunEnv runArgs { dependencies, purs } executeDir = do
{ workspace, logOptions, rootPath } <- ask
logDebug $ "Run args: " <> show runArgs

Expand Down Expand Up @@ -824,7 +899,7 @@ mkRunEnv runArgs { dependencies, purs } = do
runOptions =
{ moduleName
, execArgs
, executeDir: Path.toGlobal rootPath
, executeDir: fromMaybe (Path.toGlobal rootPath) executeDir
, successMessage: Nothing
, failureMessage: "Running failed."
}
Expand Down
7 changes: 6 additions & 1 deletion src/Spago/Command/Script.purs
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
module Spago.Command.Script where
module Spago.Command.Script
( moduleName
) where

moduleName :: String
moduleName = "Main"
10 changes: 10 additions & 0 deletions test-fixtures/spago-script-make-file.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module Main where

import Prelude
import Effect (Effect)
import Node.FS.Sync (writeTextFile)
import Node.Encoding as Encoding

main :: Effect Unit
main = do
writeTextFile Encoding.UTF8 "spago-script-result.txt" "HELLO\n"
1 change: 1 addition & 0 deletions test-fixtures/spago-script-result.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
HELLO
2 changes: 2 additions & 0 deletions test/Spago.purs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import Test.Spago.Publish as Publish
import Test.Spago.Registry as Registry
import Test.Spago.Repl as Repl
import Test.Spago.Run as Run
import Test.Spago.Script as Script
import Test.Spago.Sources as Sources
import Test.Spago.Test as Test
import Test.Spago.Transfer as Transfer
Expand Down Expand Up @@ -69,6 +70,7 @@ main = do
Ls.spec cmdLocks
Repl.spec
Run.spec cmdLocks
Script.spec cmdLocks
Test.spec cmdLocks
Bundle.spec cmdLocks
Registry.spec
Expand Down
15 changes: 15 additions & 0 deletions test/Spago/Script.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module Test.Spago.Script where

import Test.Prelude

import Spago.Path as Path
import Test.Spec (Spec)
import Test.Spec as Spec

spec :: CommandLocks -> Spec Unit
spec locks = Spec.parallel $ Spec.around (withBuildLock locks) do
Spec.describe "script" do
Spec.it "runs a standalone source file from the caller's directory" \{ spago, fixture, testCwd } -> do
let source = fixture "spago-script-make-file.purs"
spago [ "script", "-d", "node-fs", Path.toRaw source ] >>= shouldBeSuccess
checkFixture (testCwd </> "spago-script-result.txt") (fixture "spago-script-result.txt")