mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-20 01:07:18 +03:00
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:
parent
1cd5bb2492
commit
365ac2f94c
@ -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 ()
|
||||
|
@ -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 ()
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user