Skip to content
This repository was archived by the owner on Aug 1, 2023. It is now read-only.

Commit 7d7cafc

Browse files
Hiroto Shioiksaric
authored andcommitted
Hiroto/update system (#231)
* Update gitignore, Makefile * Implement runUpdater * Add fileDoesNotExist logic * Use UpdaterData datatype * Add windowdUpdater runner * Update nix file * Update updater * Update * Add test case for updater * Fix indentation * Fix * Fix test case
1 parent 28a3271 commit 7d7cafc

File tree

11 files changed

+209
-11
lines changed

11 files changed

+209
-11
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
dist/*
55
dist-newstyle/*
66
.ghc.environment.*
7+
stack.yaml.lock
78

89
# From daedalus-bridge
910
node_modules/*

Makefile

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,20 @@ stylish-haskell: ## Apply stylish-haskell on all *.hs files
77
@find . -type f -name "*.hs" -not -path '.git' -not -path '*.stack-work*' -print0 | xargs -0 stylish-haskell -i
88

99
ghci: ## Run repl
10-
@stack ghci $(PROJECT_NAME):lib --haddock-deps --ghci-options=-fobject-code
10+
@stack ghci $(PROJECT_NAME):lib --haddock-deps --ghci-options=-fobject-code --nix
1111

1212
ghcid: ## Run ghcid
1313
@ghcid --command "stack ghci $(PROJECT_NAME):lib --nix -j12 --ghci-options=-fobject-code"
1414

1515
run-test: ## Build & run test
16-
@stack build --fast && \
17-
stack test --fast
16+
@stack build --fast --nix && \
17+
stack test --fast --nix
1818

1919
test-ghci: ## Run repl on test suites
20-
@stack ghci $(PROJECT_NAME):lib $(PROJECT_NAME):test:$(PROJECT_NAME)-test --ghci-options=-fobject-code
20+
@stack ghci $(PROJECT_NAME):lib $(PROJECT_NAME):test:$(PROJECT_NAME)-test --ghci-options=-fobject-code --nix
2121

2222
test-ghcid: ## Run ghcid on test suites
23-
@ghcid --command "stack ghci $(PROJECT_NAME):lib $(PROJECT_NAME):test:$(PROJECT_NAME)-test --ghci-options=-fobject-code"
23+
@ghcid --command "stack ghci --nix $(PROJECT_NAME):lib $(PROJECT_NAME):test:$(PROJECT_NAME)-test --ghci-options=-fobject-code"
2424

2525
test-ghcid-nix: ## Run ghcid on test suites with Nix
2626
#NUM_PROC = $(nproc --all) # Either try to fetch the real num of cores or default to 4

app/Launcher/Main.hs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import Formatting.Buildable (Buildable (..))
1818

1919
import Control.Exception.Safe (throwM)
2020

21+
-- import Cardano.Shell.Update.Lib (runUpdater, updaterData)
2122
import Cardano.Shell.Configuration.Types (LauncherConfig (..),
2223
WalletArguments (..),
2324
WalletPath (..))
@@ -37,7 +38,7 @@ import Data.X509.Extra (failIfReasons, genRSA256KeyPair,
3738

3839
main :: IO ()
3940
main = do
40-
41+
4142
let launcherConfig :: LauncherConfig
4243
launcherConfig = LauncherConfig
4344
{ lcfgFilePath = "./configuration/cert-configuration.yaml"

cardano-shell.cabal

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ library
3131
-- Update system
3232
, CardanoShellSpec
3333
, Cardano.Shell.Update.Types
34+
, Cardano.Shell.Update.Lib
3435
-- Constants
3536
, Cardano.Shell.Constants.PartialTypes
3637
, Cardano.Shell.Constants.Types
@@ -42,7 +43,6 @@ library
4243
Cardano.Shell.NodeIPC.Lib
4344
, Cardano.Shell.NodeIPC.Message
4445
, Cardano.Shell.NodeIPC.ServerExample
45-
4646
hs-source-dirs:
4747
app
4848
, src
@@ -69,6 +69,8 @@ library
6969
, text
7070
, transformers
7171
, generic-monoid
72+
if os(windows)
73+
build-depends: Win32
7274

7375
default-language: Haskell2010
7476
default-extensions: NoImplicitPrelude
@@ -181,6 +183,7 @@ test-suite cardano-shell-test
181183
other-modules:
182184
Paths_cardano_shell
183185
DhallConfigSpec
186+
UpdaterSpec
184187
if !os(windows)
185188
other-modules:
186189
NodeIPCSpec

nix/.stack.nix/cardano-shell.nix

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Cardano/Shell/Update/Lib.hs

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
{-| Update module
2+
-}
3+
4+
{-# LANGUAGE CPP #-}
5+
{-# LANGUAGE LambdaCase #-}
6+
{-# LANGUAGE ScopedTypeVariables #-}
7+
8+
module Cardano.Shell.Update.Lib
9+
( UpdaterData(..)
10+
, UpdateError(..)
11+
, RunCmdFunc
12+
, updaterData
13+
, runUpdater
14+
, runUpdater'
15+
) where
16+
17+
import Cardano.Prelude
18+
19+
import qualified Data.Text as T
20+
import Distribution.System (OS (..), buildOS)
21+
import Prelude (String)
22+
import System.Directory (doesFileExist, removeFile)
23+
import System.Environment (getExecutablePath)
24+
import System.Process (proc, waitForProcess, withCreateProcess)
25+
#ifdef mingw32_HOST_OS
26+
import System.Win32.Process (getCurrentProcessId)
27+
#endif
28+
29+
-- | Updater path, args, windows runner path, archive path
30+
data UpdaterData = UpdaterData
31+
{ udPath :: !FilePath
32+
, udArgs :: ![Text]
33+
, udArchivePath :: !FilePath
34+
}
35+
36+
-- Windows: https://github.com/input-output-hk/daedalus/blob/develop/installers/dhall/win64.dhall#L32-L35
37+
-- MacOS: https://github.com/input-output-hk/daedalus/blob/develop/installers/dhall/macos64.dhall#L31-L34
38+
-- Linux: https://github.com/input-output-hk/daedalus/blob/develop/installers/dhall/linux64.dhall#L29-L32
39+
updaterData :: UpdaterData
40+
updaterData = case buildOS of
41+
Windows -> UpdaterData
42+
"Installer.exe"
43+
[]
44+
"Installer.bat"
45+
OSX -> UpdaterData
46+
"/usr/bin/open"
47+
["-FW"]
48+
"\\${HOME}/Library/Application Support/Daedalus/installer.pkg"
49+
_ -> UpdaterData
50+
"/bin/update-runner"
51+
[]
52+
"\\${XDG_DATA_HOME}/Daedalus/installer.sh"
53+
54+
data UpdateError
55+
= UpdateFailed Int
56+
| UpdaterDoesNotExist
57+
deriving (Eq, Show)
58+
59+
-- | Run the update system
60+
--
61+
-- For UNIX system:
62+
--
63+
-- Check that @udPath@ exists, then run the command @udPath udArgs udArchivePath@
64+
--
65+
-- For Windows:
66+
--
67+
-- Check that @udPath@ exists, but instead of running the command directly, you
68+
-- first have to generate a @.bat@ file which will act as a script.
69+
-- After it being generated, you run that script.
70+
runUpdater :: UpdaterData -> IO (Either UpdateError ExitCode)
71+
runUpdater = runUpdater' runCmd
72+
where
73+
runCmd :: FilePath -> [String] -> FilePath -> IO ExitCode
74+
runCmd path args archive =
75+
withCreateProcess (proc path (args <> [archive]))
76+
$ \_in _out _err ph -> waitForProcess ph
77+
78+
type RunCmdFunc
79+
= FilePath
80+
-> [String]
81+
-> FilePath
82+
-> IO ExitCode
83+
84+
-- | @runUpdater@ but can inject any runCommand function.
85+
-- This is used for testing.
86+
runUpdater' :: RunCmdFunc -> UpdaterData -> IO (Either UpdateError ExitCode)
87+
runUpdater' runCommand ud = do
88+
let path = udPath ud
89+
let args = map toS $ udArgs ud
90+
let archive = (udArchivePath ud)
91+
updaterExists <- doesFileExist path
92+
if updaterExists
93+
then do
94+
exitCode <- case buildOS of
95+
Windows -> do
96+
writeWindowsUpdaterRunner archive
97+
runCommand archive args archive
98+
_ -> runCommand path args archive
99+
case exitCode of
100+
ExitSuccess -> do
101+
whenM (doesFileExist archive) $ removeFile archive
102+
return . Right $ ExitSuccess
103+
ExitFailure code -> return . Left $ UpdateFailed code
104+
else
105+
return . Left $ UpdaterDoesNotExist
106+
107+
-- | Create @.bat@ file on given @FilePath@
108+
--
109+
-- https://github.com/input-output-hk/cardano-sl/blob/develop/tools/src/launcher/Main.hs#L585
110+
--
111+
-- The installer cant write to cardano-launcher.exe while it is running
112+
-- so you must fully stop launcher before you can start the installer.
113+
-- Because of this, we need a @.bat@ file which will run the update procedure and
114+
-- re-launch the launcher.
115+
-- Only Windows has this problem.
116+
writeWindowsUpdaterRunner :: FilePath -> IO ()
117+
writeWindowsUpdaterRunner runnerPath = do
118+
exePath <- getExecutablePath
119+
launcherArgs <- getArgs
120+
#ifdef mingw32_HOST_OS
121+
selfPid <- getCurrentProcessId
122+
#else
123+
let (selfPid :: Integer) = 0 -- This will never be run on non-Windows
124+
#endif
125+
writeFile (toS runnerPath) $ T.unlines
126+
[ "TaskKill /PID "<> show selfPid <>" /F"
127+
-- Run updater
128+
, "%*"
129+
-- Delete updater
130+
, "del %1"
131+
-- Run launcher again
132+
, "start \"cardano launcher\" /b " <> (quote $ toS exePath) <> " "
133+
<> (T.unwords $ map (quote . toS) launcherArgs)
134+
-- Delete the bat file
135+
, "(goto) 2>nul & del \"%~f0\""
136+
]
137+
where
138+
quote :: Text -> Text
139+
quote str = "\"" <> str <> "\""

src/Cardano/Shell/Update/Types.hs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ module Cardano.Shell.Update.Types where
33
import Cardano.Prelude
44

55
import qualified Data.Map as M
6-
7-
import Test.QuickCheck
6+
import Test.QuickCheck (Gen, choose, frequency, listOf1)
87

98

109
-- Types we need to check the behaviour.

stack.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,5 @@ extra-deps:
3636
- containers-0.5.11.0
3737
- libsystemd-journal-1.4.4
3838

39-
4039
nix:
4140
shell-file: stack-shell.nix

test/Spec.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import Cardano.Shell.Types (CardanoFeature (..))
2020
import DhallConfigSpec (dhallConfigSpec, mkConfigSpec)
2121
import NodeIPCSMSpec (nodeIPCSMSpec)
2222
import NodeIPCSpec (nodeIPCSpec)
23+
import UpdaterSpec (updaterSpec)
2324

2425
-- | Entry point for tests.
2526
main :: IO ()
@@ -29,6 +30,7 @@ main = hspec $ do
2930
describe "Cardano configurations" mkConfigSpec
3031
describe "NodeIPC state machine" nodeIPCSMSpec
3132
describe "NodeIPC" nodeIPCSpec
33+
describe "Update system" updaterSpec
3234

3335
-- | A valid concurrency specification.
3436
validConcurrencySpec :: Spec

test/UpdaterSpec.hs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{-# LANGUAGE ScopedTypeVariables #-}
2+
3+
module UpdaterSpec where
4+
5+
import Cardano.Prelude
6+
7+
import Prelude (String)
8+
import Test.Hspec (Spec, describe, it)
9+
import Test.Hspec.QuickCheck (prop)
10+
import Test.QuickCheck (Arbitrary (..), elements)
11+
import Test.QuickCheck.Monadic (assert, monadicIO, run)
12+
13+
import Cardano.Shell.Update.Lib (UpdateError (..), UpdaterData (..),
14+
runUpdater, runUpdater')
15+
16+
updaterSpec :: Spec
17+
updaterSpec = describe "Update system" $ do
18+
it "should be successful" $ monadicIO $ do
19+
eExitCode <- run $ runUpdater testUpdaterData
20+
assert $ eExitCode == (Right ExitSuccess)
21+
22+
it "should return error when updater is not found" $ monadicIO $ do
23+
eExitCode <- run $ runUpdater testUpdaterDataNoPath
24+
assert $ eExitCode == (Left UpdaterDoesNotExist)
25+
26+
prop "should return expected error" $ \(exitNum :: ExitNum) -> monadicIO $ do
27+
eExitCode <- run $ runUpdater' (testRunCmd exitNum) testUpdaterData
28+
assert $ eExitCode == (Left . UpdateFailed . getExitNum $ exitNum)
29+
30+
testUpdaterData :: UpdaterData
31+
testUpdaterData =
32+
UpdaterData
33+
"./test/testUpdater.sh"
34+
[]
35+
""
36+
37+
testUpdaterDataNoPath :: UpdaterData
38+
testUpdaterDataNoPath =
39+
UpdaterData
40+
"This path does not exist"
41+
[]
42+
""
43+
44+
testRunCmd :: ExitNum -> FilePath -> [String] -> FilePath -> IO ExitCode
45+
testRunCmd (ExitNum num) _ _ _ = return $ ExitFailure num
46+
47+
newtype ExitNum = ExitNum {
48+
getExitNum :: Int
49+
} deriving Show
50+
51+
-- http://tldp.org/LDP/abs/html/exitcodes.html
52+
instance Arbitrary ExitNum where
53+
arbitrary = ExitNum <$> elements [1, 2, 126, 127, 128, 130, 255]

test/testUpdater.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
exit 0

0 commit comments

Comments
 (0)