Use published extension by default in daml studio. (#1965)

* Use published extension by default in daml studio.

* Implement suggestions.

* Add release notes.
This commit is contained in:
Fran 2019-07-02 11:29:19 +01:00 committed by GitHub
parent 1cd5bb2492
commit 365ac2f94c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 162 additions and 59 deletions

View File

@ -46,8 +46,8 @@ commandParser =
where damlStudioCmd = DamlStudio
<$> option readReplacement
(long "replace" <>
help "Whether an existing extension should be overwritten. ('never', 'newer' or 'always', defaults to newer)" <>
value ReplaceExtNewer
help "Whether an existing extension should be overwritten. ('never', 'newer' or 'always' for bundled extension version, 'published' for official published version of extension, defaults to 'published')" <>
value ReplaceExtPublished
)
<*> many (argument str (metavar "ARG"))
runJarCmd = RunJar
@ -75,6 +75,7 @@ commandParser =
"never" -> Just ReplaceExtNever
"newer" -> Just ReplaceExtNewer
"always" -> Just ReplaceExtAlways
"published" -> Just ReplaceExtPublished
_ -> Nothing
runCommand :: Command -> IO ()

View File

@ -34,6 +34,7 @@ import Control.Exception.Safe
import Control.Monad
import Control.Monad.Extra hiding (fromMaybeM)
import Control.Monad.Loops (untilJust)
import Data.Either.Extra
import Data.Maybe
import Data.List.Extra
import qualified Data.ByteString as BS
@ -84,53 +85,165 @@ data ReplaceExtension
-- ^ Replace the extension if the current extension is newer.
| ReplaceExtAlways
-- ^ Always replace the extension.
| ReplaceExtPublished
-- ^ Replace with published extension (the default).
-- | Run VS Code command with arguments, returning the exit code, stdout & stderr.
runVsCodeCommand :: [String] -> IO (ExitCode, String, String)
runVsCodeCommand args = do
originalEnv <- getEnvironment
let strippedEnv = filter ((`notElem` damlEnvVars) . fst) originalEnv
-- ^ Strip DAML environment variables before calling VSCode, to
-- prevent setting DAML_SDK_VERSION too early. See issue #1666.
commandEnv = addVsCodeToPath strippedEnv
-- ^ Ensure "code" is in PATH before running command.
command = unwords ("code" : args)
process = (shell command) { env = Just commandEnv }
readCreateProcessWithExitCode process ""
-- | Add VSCode bin path to environment PATH. Only need to add it on Mac, as
-- VSCode is installed in PATH by default on the other platforms.
addVsCodeToPath :: [(String, String)] -> [(String,String)]
addVsCodeToPath env | isMac =
let pathM = lookup "PATH" env
newSearchPath = maybe "" (<> [searchPathSeparator]) pathM <>
"/Applications/Visual\\ Studio\\ Code.app/Contents/Resources/app/bin"
in ("PATH", newSearchPath) : filter ((/= "PATH") . fst) env
addVsCodeToPath env = env
-- | Directory where bundled extension gets installed.
getVsCodeExtensionsDir :: IO FilePath
getVsCodeExtensionsDir = fmap (</> ".vscode/extensions") getHomeDirectory
-- | Name of VS Code extension in the marketplace.
publishedExtensionName :: String
publishedExtensionName = "DigitalAssetHoldingsLLC.daml"
-- | Name of VS Code extension bundled with the SDK. Legacy, but also
-- useful in a pinch, in case published extension goes bad.
bundledExtensionName :: String
bundledExtensionName = "da-vscode-daml-extension"
-- | Status of installed VS Code extensions.
data InstalledExtensions = InstalledExtensions
{ bundledExtensionVersion :: Maybe SdkVersion
-- ^ bundled extension version, if installed
, publishedExtensionIsInstalled :: Bool
-- ^ true if published extension is installed
} deriving (Show, Eq)
-- | Get status of installed VS code extensions.
getInstalledExtensions :: IO InstalledExtensions
getInstalledExtensions = do
extensionsDir <- getVsCodeExtensionsDir
let bundledExtensionDir = extensionsDir </> bundledExtensionName
bundledExtensionVersion <- readVersionFile bundledExtensionDir
(_exitCode, extensionsStr, _err) <- runVsCodeCommand ["--list-extensions"]
let extensions = lines extensionsStr
publishedExtensionIsInstalled = publishedExtensionName `elem` extensions
pure InstalledExtensions {..}
-- | Read VERSION file in directory. Returns Nothing on failure.
readVersionFile :: FilePath -> IO (Maybe SdkVersion)
readVersionFile dir = do
versionStrE <- tryIO $ readFileUTF8 (dir </> "VERSION")
pure $ do
versionStr <- eitherToMaybe versionStrE
eitherToMaybe . parseVersion . T.strip . T.pack $ versionStr
runDamlStudio :: ReplaceExtension -> [String] -> IO ()
runDamlStudio replaceExt remainingArguments = do
sdkPath <- getSdkPath
vscodeExtensionsDir <- fmap (</> ".vscode/extensions") getHomeDirectory
let vscodeExtensionName = "da-vscode-daml-extension"
let vscodeExtensionSrcDir = sdkPath </> "studio"
let vscodeExtensionTargetDir = vscodeExtensionsDir </> vscodeExtensionName
whenM (shouldReplaceExtension replaceExt vscodeExtensionTargetDir) $
removePathForcibly vscodeExtensionTargetDir
installExtension vscodeExtensionSrcDir vscodeExtensionTargetDir
-- Note that it is important that we use `shell` rather than `proc` here as
-- `proc` will look for `code.exe` in PATH which does not exist.
projectPathM <- getProjectPath
let codeCommand
| isMac = "open -a \"Visual Studio Code\""
| otherwise = "code"
path = fromMaybe "." projectPathM
command = unwords $ codeCommand : path : remainingArguments
vscodeExtensionsDir <- getVsCodeExtensionsDir
InstalledExtensions {..} <- getInstalledExtensions
-- Strip DAML environment variables before calling vscode.
originalEnv <- getEnvironment
let strippedEnv = filter ((`notElem` damlEnvVars) . fst) originalEnv
process = (shell command) { env = Just strippedEnv }
let bundledExtensionSource = sdkPath </> "studio"
bundledExtensionTarget = vscodeExtensionsDir </> bundledExtensionName
exitCode <- withCreateProcess process $ \_ _ _ -> waitForProcess
when (exitCode /= ExitSuccess) $
hPutStrLn stderr $
"Failed to launch Visual Studio Code." <>
" See https://code.visualstudio.com/Download for installation instructions."
exitWith exitCode
removeBundledExtension =
when (isJust bundledExtensionVersion) $
removePathForcibly bundledExtensionTarget
shouldReplaceExtension :: ReplaceExtension -> FilePath -> IO Bool
shouldReplaceExtension replaceExt dir =
removePublishedExtension =
when publishedExtensionIsInstalled $ do
(exitCode, _out, err) <- runVsCodeCommand
["--uninstall-extension", publishedExtensionName]
when (exitCode /= ExitSuccess) $ do
hPutStrLn stderr . unlines $
[ err
, "Failed to uninstall published version of DAML Studio."
]
exitWith exitCode
installBundledExtension' =
installBundledExtension bundledExtensionSource bundledExtensionTarget
installPublishedExtension = do
when (not publishedExtensionIsInstalled) $ do
(exitCode, _out, err) <- runVsCodeCommand
["--install-extension", publishedExtensionName]
when (exitCode /= ExitSuccess) $ do
hPutStr stderr . unlines $
[ err
, "Failed to install DAML Studio extension from marketplace."
, "Installing bundled DAML Studio extension instead."
]
installBundledExtension'
-- First, ensure extension is installed as requested.
case replaceExt of
ReplaceExtNever -> pure False
ReplaceExtAlways -> pure True
ReplaceExtNever ->
when (isNothing bundledExtensionVersion)
installPublishedExtension
ReplaceExtAlways -> do
removePublishedExtension
removeBundledExtension
installBundledExtension'
ReplaceExtNewer -> do
let installedVersionFile = dir </> "VERSION"
ifM (doesFileExist installedVersionFile)
(do installedVersion <-
requiredE "Failed to parse version of VSCode extension" . parseVersion . T.strip . T.pack =<<
readFileUTF8 installedVersionFile
sdkVersion <- requiredE "Failed to parse SDK version" . parseVersion . T.pack =<< getSdkVersion
pure (sdkVersion > installedVersion))
(pure True)
-- ^ If the VERSION file does not exist, we must have installed an older version.
sdkVersion <- requiredE "Failed to parse SDK version"
. parseVersion . T.pack =<< getSdkVersion
removePublishedExtension
when (maybe True (< sdkVersion) bundledExtensionVersion) $ do
removeBundledExtension
installBundledExtension'
ReplaceExtPublished -> do
removeBundledExtension
installPublishedExtension
-- Then, open visual studio code.
projectPathM <- getProjectPath
let path = fromMaybe "." projectPathM
(exitCode, _out, err) <- runVsCodeCommand (path : remainingArguments)
when (exitCode /= ExitSuccess) $ do
hPutStrLn stderr . unlines $
[ err
, "Failed to launch DAML studio. Make sure Visual Studio Code is installed."
, "See https://code.visualstudio.com/Download for installation instructions."
]
exitWith exitCode
installBundledExtension :: FilePath -> FilePath -> IO ()
installBundledExtension src target = do
-- Create .vscode/extensions if it does not already exist.
createDirectoryIfMissing True (takeDirectory target)
catchJust
(guard . isAlreadyExistsError)
install
(const $ pure ())
-- If we get an exception, we just keep the existing extension.
where
install
| isWindows = do
-- We create the directory to throw an isAlreadyExistsError.
createDirectory target
copyDirectory src target
| otherwise = createDirectoryLink src target
runJar :: FilePath -> [String] -> IO ()
runJar jarPath remainingArguments = do
@ -573,23 +686,6 @@ getProjectConfig = do
projectPath <- required "Must be called from within a project" =<< getProjectPath
readProjectConfig (ProjectPath projectPath)
installExtension :: FilePath -> FilePath -> IO ()
installExtension src target = do
-- Create .vscode/extensions if it does not already exist.
createDirectoryIfMissing True (takeDirectory target)
catchJust
(guard . isAlreadyExistsError)
install
(const $ pure ())
-- If we get an exception, we just keep the existing extension.
where
install
| isWindows = do
-- We create the directory to throw an isAlreadyExistsError.
createDirectory target
copyDirectory src target
| otherwise = createDirectoryLink src target
-- | `waitForConnectionOnPort sleep port` keeps trying to establish a TCP connection on the given port.
-- Between each connection request it calls `sleep`.
waitForConnectionOnPort :: IO () -> Int -> IO ()

View File

@ -15,9 +15,15 @@ HEAD — ongoing
- [Scala bindings] Contract keys are exposed on CreatedEvent. See `#1681 <https://github.com/digital-asset/daml/issues/1681>`__.
- [Navigator] Contract keys are show in the contract details page. See `#1681 <https://github.com/digital-asset/daml/issues/1681>`__.
- [DAML Standard Library] **BREAKING CHANGE**: Remove the deprecated modules ``DA.Map``, ``DA.Set``, ``DA.Experimental.Map`` and ``DA.Experimental.Set``. Please use ``DA.Next.Map`` and ``DA.Next.Set`` instead.
- [Sandbox] Fixed an issue when CompletionService returns offsets having inclusive semantics when used for re-subscription.
- [Sandbox] Fixed an issue when CompletionService returns offsets having inclusive semantics when used for re-subscription.
See `#1932 <https://github.com/digital-asset/daml/pull/1932>`__.
- [DAML Compiler] The default output path for all artifacts is now in the ``.daml`` directory.
In particular, the default output path for .dar files in ``daml build`` is now
``.daml/dist/<projectname>.dar``.
- [DAML Studio] DAML Studio is now published as an extension in the Visual Studio Code
marketplace. The ``daml studio`` command will now install the published extension by
default, but will revert to the extension bundled with the DAML SDK if installation
fails. You can get the old default behavior of always using the bundled extension
by running ``daml studio --replace=newer`` or ``daml studio --replace=always`` instead.