packages feed

hwm 0.1.0 → 0.1.1

raw patch · 29 files changed

+317/−238 lines, 29 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- HWM.CLI.Command.Environment: EnvAdd :: EnvAddOptions -> EnvCommand
- HWM.CLI.Command.Environment: EnvLs :: EnvLsOptions -> EnvCommand
- HWM.CLI.Command.Environment: EnvRemove :: EnvRemoveOptions -> EnvCommand
- HWM.CLI.Command.Environment: EnvSetDefault :: EnvSetDefaultOptions -> EnvCommand
- HWM.CLI.Command.Environment: data EnvCommand
- HWM.CLI.Command.Environment: instance GHC.Show.Show HWM.CLI.Command.Environment.EnvCommand
- HWM.CLI.Command.Environment: instance HWM.Core.Parsing.ParseCLI HWM.CLI.Command.Environment.EnvCommand
- HWM.CLI.Command.Environment: runEnv :: EnvCommand -> ConfigT ()
- HWM.CLI.Command.Registry: RegistryAdd :: RegistryAddOptions -> RegistryCommand
- HWM.CLI.Command.Registry: RegistryAudit :: RegistryAuditOptions -> RegistryCommand
- HWM.CLI.Command.Registry: RegistryLs :: RegistryLsOptions -> RegistryCommand
- HWM.CLI.Command.Registry: data RegistryCommand
- HWM.CLI.Command.Registry: instance GHC.Show.Show HWM.CLI.Command.Registry.RegistryCommand
- HWM.CLI.Command.Registry: instance HWM.Core.Parsing.ParseCLI HWM.CLI.Command.Registry.RegistryCommand
- HWM.CLI.Command.Registry: runRegistry :: RegistryCommand -> ConfigT ()
- HWM.CLI.Command.Release.Artifacts: [ghPublishUrl] :: ReleaseArchiveOptions -> Maybe Text
- HWM.CLI.Command.Release.Artifacts: [overrides] :: ReleaseArchiveOptions -> ArchiveOverrides
- HWM.CLI.Command.Release.Artifacts: instance GHC.Show.Show HWM.CLI.Command.Release.Artifacts.ArchiveOverrides
- HWM.CLI.Command.Release.Artifacts: instance HWM.Core.Parsing.ParseCLI HWM.CLI.Command.Release.Artifacts.ArchiveOverrides
- HWM.CLI.Command.Workspace: WorkspaceAdd :: WorkspaceAddOptions -> WorkspaceCommand
- HWM.CLI.Command.Workspace: WorkspaceLs :: WorkspaceLsOptions -> WorkspaceCommand
- HWM.CLI.Command.Workspace: data WorkspaceCommand
- HWM.CLI.Command.Workspace: instance GHC.Show.Show HWM.CLI.Command.Workspace.WorkspaceCommand
- HWM.CLI.Command.Workspace: instance HWM.Core.Parsing.ParseCLI HWM.CLI.Command.Workspace.WorkspaceCommand
- HWM.CLI.Command.Workspace: runWorkspace :: WorkspaceCommand -> ConfigT ()
- HWM.Runtime.Logging: log :: MonadIO m => Name -> [(Text, Text)] -> Text -> m FilePath
- HWM.Runtime.Logging: logError :: MonadIO m => Name -> [(Text, Text)] -> Text -> m FilePath
- HWM.Runtime.Logging: logPath :: Name -> FilePath
- HWM.Runtime.Logging: logRoot :: FilePath
- HWM.Runtime.UI: mapMTable :: MonadUI m => Text -> [(Name, m (Text, a))] -> m [(Name, a)]
+ HWM.CLI.Command.Environment.Root: EnvAdd :: EnvAddOptions -> EnvCommand
+ HWM.CLI.Command.Environment.Root: EnvLs :: EnvLsOptions -> EnvCommand
+ HWM.CLI.Command.Environment.Root: EnvRemove :: EnvRemoveOptions -> EnvCommand
+ HWM.CLI.Command.Environment.Root: EnvSetDefault :: EnvSetDefaultOptions -> EnvCommand
+ HWM.CLI.Command.Environment.Root: data EnvCommand
+ HWM.CLI.Command.Environment.Root: instance GHC.Show.Show HWM.CLI.Command.Environment.Root.EnvCommand
+ HWM.CLI.Command.Environment.Root: instance HWM.Core.Parsing.ParseCLI HWM.CLI.Command.Environment.Root.EnvCommand
+ HWM.CLI.Command.Environment.Root: runEnv :: EnvCommand -> ConfigT ()
+ HWM.CLI.Command.Registry.Root: RegistryAdd :: RegistryAddOptions -> RegistryCommand
+ HWM.CLI.Command.Registry.Root: RegistryAudit :: RegistryAuditOptions -> RegistryCommand
+ HWM.CLI.Command.Registry.Root: RegistryLs :: RegistryLsOptions -> RegistryCommand
+ HWM.CLI.Command.Registry.Root: data RegistryCommand
+ HWM.CLI.Command.Registry.Root: instance GHC.Show.Show HWM.CLI.Command.Registry.Root.RegistryCommand
+ HWM.CLI.Command.Registry.Root: instance HWM.Core.Parsing.ParseCLI HWM.CLI.Command.Registry.Root.RegistryCommand
+ HWM.CLI.Command.Registry.Root: runRegistry :: RegistryCommand -> ConfigT ()
+ HWM.CLI.Command.Release.Artifacts: [ghPublish] :: ReleaseArchiveOptions -> Bool
+ HWM.CLI.Command.Release.Artifacts: [ovGhcOptions] :: ReleaseArchiveOptions -> Maybe [Text]
+ HWM.CLI.Command.Release.Artifacts: [ovNameTemplate] :: ReleaseArchiveOptions -> Maybe Text
+ HWM.CLI.Command.Workspace.Root: WorkspaceAdd :: WorkspaceAddOptions -> WorkspaceCommand
+ HWM.CLI.Command.Workspace.Root: WorkspaceLs :: WorkspaceLsOptions -> WorkspaceCommand
+ HWM.CLI.Command.Workspace.Root: data WorkspaceCommand
+ HWM.CLI.Command.Workspace.Root: instance GHC.Show.Show HWM.CLI.Command.Workspace.Root.WorkspaceCommand
+ HWM.CLI.Command.Workspace.Root: instance HWM.Core.Parsing.ParseCLI HWM.CLI.Command.Workspace.Root.WorkspaceCommand
+ HWM.CLI.Command.Workspace.Root: runWorkspace :: WorkspaceCommand -> ConfigT ()
+ HWM.Core.Options: whenCI :: MonadIO m => m () -> m ()
+ HWM.Domain.Config: [cfgGithub] :: Config -> Maybe Text
+ HWM.Domain.Release: getArtifact :: MonadError Issue m => Name -> ReleaseArtifactConfigs -> m ArtifactConfig
+ HWM.Domain.Release: selectedArtifacts :: MonadError Issue m => Maybe Name -> ReleaseArtifactConfigs -> m [(Name, ArtifactConfig)]
+ HWM.Domain.Release: type ReleaseArtifactConfigs = Map Name ArtifactConfig
+ HWM.Integrations.Toolchain.Github: ensureIsLatestTag :: (MonadIO m, MonadError Issue m) => Version -> m Text
+ HWM.Runtime.Logging: logIssue :: (MonadIO m, MonadUI m) => Name -> Severity -> [(Text, Text)] -> Text -> m FilePath
+ HWM.Runtime.Network: getGHUploadUrl :: (MonadIO m, MonadError Issue m) => Config -> Text -> m Text
+ HWM.Runtime.Network: instance Data.Aeson.Types.FromJSON.FromJSON HWM.Runtime.Network.GitHubRelease
+ HWM.Runtime.Network: instance GHC.Generics.Generic HWM.Runtime.Network.GitHubRelease
+ HWM.Runtime.Network: instance GHC.Show.Show HWM.Runtime.Network.GitHubRelease
+ HWM.Runtime.UI: forTable_ :: MonadUI m => [a] -> (a -> (Text, m Text)) -> m ()
- HWM.CLI.Command.Release.Artifacts: ReleaseArchiveOptions :: Maybe Text -> Maybe Text -> FilePath -> Maybe [Text] -> ArchiveOverrides -> ReleaseArchiveOptions
+ HWM.CLI.Command.Release.Artifacts: ReleaseArchiveOptions :: Maybe Text -> Bool -> FilePath -> Maybe [Text] -> Maybe [Text] -> Maybe Text -> ReleaseArchiveOptions
- HWM.Domain.Config: Config :: Name -> Version -> Maybe Bounds -> Workspace -> Environments -> Dependencies -> Map Name Text -> Maybe Release -> Config
+ HWM.Domain.Config: Config :: Name -> Maybe Text -> Version -> Maybe Bounds -> Workspace -> Environments -> Dependencies -> Map Name Text -> Maybe Release -> Config
- HWM.Runtime.UI: forTable :: MonadUI m => Int -> [a] -> (a -> (Text, Text)) -> m ()
+ HWM.Runtime.UI: forTable :: MonadUI m => Name -> [a] -> (a -> (Name, m (Name, b))) -> m [(Name, b)]
- HWM.Runtime.UI: sectionConfig :: MonadUI m => Int -> [(Text, m Text)] -> m ()
+ HWM.Runtime.UI: sectionConfig :: MonadUI m => [(Text, m Text)] -> m ()
- HWM.Runtime.UI: sectionTableM :: MonadUI m => Int -> Text -> [(Text, m Text)] -> m ()
+ HWM.Runtime.UI: sectionTableM :: MonadUI m => Text -> [(Text, m Text)] -> m ()

Files

hwm.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           hwm-version:        0.1.0+version:        0.1.1 synopsis:       Haskell Workspace Manager - Orchestrates Stack, Cabal, and HLS description:    HWM (Haskell Workspace Manager) manages multi-package Haskell projects by                 generating and synchronizing configuration files for Stack, Cabal, Hpack, and HLS@@ -32,16 +32,16 @@   exposed-modules:       HWM.CLI.App       HWM.CLI.Command-      HWM.CLI.Command.Environment       HWM.CLI.Command.Environment.Add       HWM.CLI.Command.Environment.Ls       HWM.CLI.Command.Environment.Remove+      HWM.CLI.Command.Environment.Root       HWM.CLI.Command.Environment.SetDefault       HWM.CLI.Command.Init-      HWM.CLI.Command.Registry       HWM.CLI.Command.Registry.Add       HWM.CLI.Command.Registry.Audit       HWM.CLI.Command.Registry.Ls+      HWM.CLI.Command.Registry.Root       HWM.CLI.Command.Release.Artifacts       HWM.CLI.Command.Release.Publish       HWM.CLI.Command.Release.Root@@ -49,9 +49,9 @@       HWM.CLI.Command.Status       HWM.CLI.Command.Sync       HWM.CLI.Command.Version-      HWM.CLI.Command.Workspace       HWM.CLI.Command.Workspace.Add       HWM.CLI.Command.Workspace.Ls+      HWM.CLI.Command.Workspace.Root       HWM.Core.Common       HWM.Core.Formatting       HWM.Core.Has@@ -69,6 +69,7 @@       HWM.Domain.Workspace       HWM.Integrations.Scaffold       HWM.Integrations.Toolchain.Cabal+      HWM.Integrations.Toolchain.Github       HWM.Integrations.Toolchain.Hie       HWM.Integrations.Toolchain.Lib       HWM.Integrations.Toolchain.Package
src/HWM/CLI/App.hs view
@@ -71,7 +71,7 @@         "Manage workspace groups and package members.",         Workspace <$> parseCLI       ),-      ( "environment",+      ( "environments",         "Manage GHC toolchains and resolver targets.",         Env <$> parseCLI       ),
src/HWM/CLI/Command.hs view
@@ -12,15 +12,15 @@ where  import Data.Version (showVersion)-import HWM.CLI.Command.Environment (EnvCommand, runEnv)+import HWM.CLI.Command.Environment.Root (EnvCommand, runEnv) import HWM.CLI.Command.Init (InitOptions (..), initWorkspace)-import HWM.CLI.Command.Registry (RegistryCommand, runRegistry)+import HWM.CLI.Command.Registry.Root (RegistryCommand, runRegistry) import HWM.CLI.Command.Release.Root (ReleaseCommand (..), runRelease) import HWM.CLI.Command.Run (ScriptOptions, runScript) import HWM.CLI.Command.Status (showStatus) import HWM.CLI.Command.Sync (sync) import HWM.CLI.Command.Version (VersionOptions, runVersion)-import HWM.CLI.Command.Workspace (WorkspaceCommand, runWorkspace)+import HWM.CLI.Command.Workspace.Root (WorkspaceCommand, runWorkspace) import HWM.Core.Common (Name) import HWM.Core.Options (Options (..), defaultOptions) import HWM.Core.Version (Bump (..))
− src/HWM/CLI/Command/Environment.hs
@@ -1,42 +0,0 @@-{-# LANGUAGE NoImplicitPrelude #-}--module HWM.CLI.Command.Environment-  ( EnvCommand (..),-    runEnv,-  )-where--import HWM.CLI.Command.Environment.Add (EnvAddOptions, runEnvAdd)-import HWM.CLI.Command.Environment.Ls (EnvLsOptions, runEnvLs)-import HWM.CLI.Command.Environment.Remove (EnvRemoveOptions, runEnvRemove)-import HWM.CLI.Command.Environment.SetDefault (EnvSetDefaultOptions, runEnvSetDefault)-import HWM.Core.Parsing (ParseCLI (..))-import HWM.Domain.ConfigT (ConfigT)-import Options.Applicative (command, info, progDesc, subparser)-import Relude---- | Subcommands for `hwm environment`-data EnvCommand-  = EnvAdd EnvAddOptions-  | EnvRemove EnvRemoveOptions-  | EnvSetDefault EnvSetDefaultOptions-  | EnvLs EnvLsOptions-  deriving (Show)--runEnv :: EnvCommand -> ConfigT ()-runEnv cmd = case cmd of-  EnvAdd opts -> runEnvAdd opts-  EnvRemove opts -> runEnvRemove opts-  EnvSetDefault opts -> runEnvSetDefault opts-  EnvLs opts -> runEnvLs opts--instance ParseCLI EnvCommand where-  parseCLI =-    subparser-      ( mconcat-          [ command "add" (info (EnvAdd <$> parseCLI) (progDesc "Add a new environment.")),-            command "remove" (info (EnvRemove <$> parseCLI) (progDesc "Remove an environment.")),-            command "set-default" (info (EnvSetDefault <$> parseCLI) (progDesc "Set the default environment.")),-            command "ls" (info (EnvLs <$> parseCLI) (progDesc "List all environments."))-          ]-      )
+ src/HWM/CLI/Command/Environment/Root.hs view
@@ -0,0 +1,42 @@+{-# LANGUAGE NoImplicitPrelude #-}++module HWM.CLI.Command.Environment.Root+  ( EnvCommand (..),+    runEnv,+  )+where++import HWM.CLI.Command.Environment.Add (EnvAddOptions, runEnvAdd)+import HWM.CLI.Command.Environment.Ls (EnvLsOptions, runEnvLs)+import HWM.CLI.Command.Environment.Remove (EnvRemoveOptions, runEnvRemove)+import HWM.CLI.Command.Environment.SetDefault (EnvSetDefaultOptions, runEnvSetDefault)+import HWM.Core.Parsing (ParseCLI (..))+import HWM.Domain.ConfigT (ConfigT)+import Options.Applicative (command, info, progDesc, subparser)+import Relude++-- | Subcommands for `hwm environment`+data EnvCommand+  = EnvAdd EnvAddOptions+  | EnvRemove EnvRemoveOptions+  | EnvSetDefault EnvSetDefaultOptions+  | EnvLs EnvLsOptions+  deriving (Show)++runEnv :: EnvCommand -> ConfigT ()+runEnv cmd = case cmd of+  EnvAdd opts -> runEnvAdd opts+  EnvRemove opts -> runEnvRemove opts+  EnvSetDefault opts -> runEnvSetDefault opts+  EnvLs opts -> runEnvLs opts++instance ParseCLI EnvCommand where+  parseCLI =+    subparser+      ( mconcat+          [ command "add" (info (EnvAdd <$> parseCLI) (progDesc "Add a new environment.")),+            command "remove" (info (EnvRemove <$> parseCLI) (progDesc "Remove an environment.")),+            command "set-default" (info (EnvSetDefault <$> parseCLI) (progDesc "Set the default environment.")),+            command "ls" (info (EnvLs <$> parseCLI) (progDesc "List all environments."))+          ]+      )
src/HWM/CLI/Command/Init.hs view
@@ -64,7 +64,8 @@     cfgWorkspace <- buildWorkspace graph pkgs     saveConfig       Config-        { cfgBounds = Nothing,+        { cfgGithub = Nothing,+          cfgBounds = Nothing,           cfgScripts = defaultScripts,           cfgRelease = Nothing,           ..
− src/HWM/CLI/Command/Registry.hs
@@ -1,39 +0,0 @@-{-# LANGUAGE NoImplicitPrelude #-}--module HWM.CLI.Command.Registry-  ( RegistryCommand (..),-    runRegistry,-  )-where--import HWM.CLI.Command.Registry.Add (RegistryAddOptions, runRegistryAdd)-import HWM.CLI.Command.Registry.Audit (RegistryAuditOptions, runRegistryAudit)-import HWM.CLI.Command.Registry.Ls (RegistryLsOptions, runRegistryLs)-import HWM.Core.Parsing (ParseCLI (..))-import HWM.Domain.ConfigT (ConfigT)-import Options.Applicative (command, info, progDesc, subparser)-import Relude---- | Subcommands for `hwm registry`-data RegistryCommand-  = RegistryAdd RegistryAddOptions-  | RegistryAudit RegistryAuditOptions-  | RegistryLs RegistryLsOptions-  deriving (Show)--runRegistry :: RegistryCommand -> ConfigT ()-runRegistry registryCommand =-  case registryCommand of-    RegistryAdd opts -> runRegistryAdd opts-    RegistryAudit opts -> runRegistryAudit opts-    RegistryLs opts -> runRegistryLs opts--instance ParseCLI RegistryCommand where-  parseCLI =-    subparser-      ( mconcat-          [ command "add" (info (RegistryAdd <$> parseCLI) (progDesc "Add a dependency to the registry.")),-            command "audit" (info (RegistryAudit <$> parseCLI) (progDesc "Audit and optionally fix the registry.")),-            command "ls" (info (RegistryLs <$> parseCLI) (progDesc "List registry entries."))-          ]-      )
src/HWM/CLI/Command/Registry/Add.hs view
@@ -31,7 +31,6 @@ runRegistryAdd RegistryAddOptions {opsPkgName, opsWorkspace} = do   workspaces <- resolveWorkspaces opsWorkspace   sectionTableM-    0     "add dependency"     [ ("package", pure $ chalk Magenta (format opsPkgName)),       ("target", pure $ chalk Cyan (if null opsWorkspace then "none (registry only)" else T.intercalate ", " opsWorkspace))@@ -47,7 +46,7 @@       let dependency = Dependency opsPkgName bounds        ((\cf -> pure cf {cfgRegistry = cfgRegistry cf <> singleDeps dependency}) `updateConfig`) $ do-        sectionConfig 0 [("hwm.yaml", pure $ chalk Green "✓")]+        sectionConfig [("hwm.yaml", pure $ chalk Green "✓")]         addDepToPackage workspaces dependency     Just bounds -> do       section "discovery" $ do
src/HWM/CLI/Command/Registry/Audit.hs view
@@ -29,7 +29,7 @@ runRegistryAudit RegistryAuditOptions {..} = do   originalRegistry <- asks (cfgRegistry . config)   range <- getTestedRange-  sectionTableM 16 "audit" [("mode", pure (if auditFix then if auditForce then chalk Yellow "fix (force)" else chalk Cyan "fix" else "check"))]+  sectionTableM "audit" [("mode", pure (if auditFix then if auditForce then chalk Yellow "fix (force)" else chalk Cyan "fix" else "check"))]    let dependencyAudits = filter (auditHasAny (/= Valid)) $ mapWithName (auditBounds range) originalRegistry   section "registry" $ printGenTable $ formatAudit <$> dependencyAudits@@ -41,7 +41,7 @@     else do       if auditFix         then ((\cf -> pure $ cf {cfgRegistry = mapDeps (updateDepBounds auditForce range) originalRegistry}) `updateConfig`) $ do-          sectionConfig 0 [("hwm.yaml", pure $ chalk Green "✓")]+          sectionConfig [("hwm.yaml", pure $ chalk Green "✓")]           syncPackages         else do           injectIssue
+ src/HWM/CLI/Command/Registry/Root.hs view
@@ -0,0 +1,39 @@+{-# LANGUAGE NoImplicitPrelude #-}++module HWM.CLI.Command.Registry.Root+  ( RegistryCommand (..),+    runRegistry,+  )+where++import HWM.CLI.Command.Registry.Add (RegistryAddOptions, runRegistryAdd)+import HWM.CLI.Command.Registry.Audit (RegistryAuditOptions, runRegistryAudit)+import HWM.CLI.Command.Registry.Ls (RegistryLsOptions, runRegistryLs)+import HWM.Core.Parsing (ParseCLI (..))+import HWM.Domain.ConfigT (ConfigT)+import Options.Applicative (command, info, progDesc, subparser)+import Relude++-- | Subcommands for `hwm registry`+data RegistryCommand+  = RegistryAdd RegistryAddOptions+  | RegistryAudit RegistryAuditOptions+  | RegistryLs RegistryLsOptions+  deriving (Show)++runRegistry :: RegistryCommand -> ConfigT ()+runRegistry registryCommand =+  case registryCommand of+    RegistryAdd opts -> runRegistryAdd opts+    RegistryAudit opts -> runRegistryAudit opts+    RegistryLs opts -> runRegistryLs opts++instance ParseCLI RegistryCommand where+  parseCLI =+    subparser+      ( mconcat+          [ command "add" (info (RegistryAdd <$> parseCLI) (progDesc "Add a dependency to the registry.")),+            command "audit" (info (RegistryAudit <$> parseCLI) (progDesc "Audit and optionally fix the registry.")),+            command "ls" (info (RegistryLs <$> parseCLI) (progDesc "List registry entries."))+          ]+      )
src/HWM/CLI/Command/Release/Artifacts.hs view
@@ -13,7 +13,6 @@ where  import Control.Monad.Except (MonadError (..))-import qualified Data.Map as Map import qualified Data.Text as T import Data.Traversable (for) import HWM.Core.Common (Name)@@ -23,13 +22,14 @@ import HWM.Core.Result (fromEither) import HWM.Domain.Config (Config (..)) import HWM.Domain.ConfigT (ConfigT, Env (..), getArchiveConfigs)-import HWM.Domain.Release (ArchiveFormat, ArtifactConfig (..))+import HWM.Domain.Release (ArchiveFormat, ArtifactConfig (..), ReleaseArtifactConfigs, selectedArtifacts) import HWM.Domain.Workspace (resolveWorkspaces)+import HWM.Integrations.Toolchain.Github (ensureIsLatestTag) import HWM.Integrations.Toolchain.Stack (stackGenBinary) import HWM.Runtime.Archive (ArchiveInfo (..), ArchivingPlan (..), createArchive)-import HWM.Runtime.Network (uploadToGitHub)-import HWM.Runtime.UI (indent, mapMTable, putLine, section, sectionTableM)-import Options.Applicative (help, long, metavar, option, showDefault, str, strOption, value)+import HWM.Runtime.Network (getGHUploadUrl, uploadToGitHub)+import HWM.Runtime.UI (forTable, indent, putLine, section, sectionTableM)+import Options.Applicative (argument, help, long, metavar, option, showDefault, str, strOption, switch, value) import Relude import System.Directory (createDirectoryIfMissing, removePathForcibly) import System.FilePath (joinPath)@@ -37,39 +37,29 @@ -- | Options for 'hwm release archive' data ReleaseArchiveOptions = ReleaseArchiveOptions   { targetName :: Maybe Text,-    ghPublishUrl :: Maybe Text,+    ghPublish :: Bool,     outputDir :: FilePath,     ovFormat :: Maybe [Text],-    overrides :: ArchiveOverrides-  }-  deriving (Show)--data ArchiveOverrides = ArchiveOverrides-  { ovGhcOptions :: Maybe [Text],+    ovGhcOptions :: Maybe [Text],     ovNameTemplate :: Maybe Text   }   deriving (Show) -instance ParseCLI ArchiveOverrides where-  parseCLI =-    ArchiveOverrides-      <$> optional (option (parseLS <$> str) (long "ghc-options" <> metavar "GHC_OPTION" <> help "Override GHC options for the release target. Can be specified multiple times."))-      <*> optional (strOption (long "name-template" <> metavar "NAME_TEMPLATE" <> help "Override the name template for the release target. Use {name} and {version} as placeholders."))- instance ParseCLI ReleaseArchiveOptions where   parseCLI =     ReleaseArchiveOptions-      <$> optional (strOption (long "target" <> metavar "TARGET" <> help "Name of the release target to build. If not specified, all targets will be built."))-      <*> optional (strOption (long "gh-upload" <> metavar "UPLOAD_URL" <> help "URL to upload the release artifact. If not specified, the artifact will not be uploaded."))+      <$> optional (argument str (metavar "TARGET" <> help "Name of the release target to build (default: all)"))+      <*> switch (long "github" <> help "Upload generated artifacts to a GitHub Release")       <*> strOption         ( long "output-dir"             <> metavar "OUTPUT_DIR"             <> help "Directory to output the release artifacts."-            <> value ".hwm/dist"+            <> value defaultOutputDir             <> showDefault         )       <*> optional (option (parseLS <$> str) (long "format" <> metavar "FORMAT" <> help "Override the archive format for the release target. Supported: zip, tar.gz."))-      <*> parseCLI+      <*> optional (option (parseLS <$> str) (long "ghc-options" <> metavar "GHC_OPTION" <> help "Override GHC options for the release target. Can be specified multiple times."))+      <*> optional (strOption (long "name-template" <> metavar "NAME_TEMPLATE" <> help "Override the name template for the release target. Use {name} and {version} as placeholders."))  genBindaryDir :: (MonadIO m, ToString a) => a -> m FilePath genBindaryDir name = do@@ -81,44 +71,45 @@ ghcOptions [] = [] ghcOptions xs = ["--ghc-options=" <> T.unwords xs] +defaultOutputDir :: FilePath+defaultOutputDir = ".hwm/dist"+ prepeareDir :: (MonadIO m) => FilePath -> m () prepeareDir dir = liftIO $ do   removePathForcibly dir   createDirectoryIfMissing True dir -applyOverrieds :: Maybe [ArchiveFormat] -> ArchiveOverrides -> ArtifactConfig -> ArtifactConfig-applyOverrieds formats ArchiveOverrides {..} cfg =-  cfg-    { arcFormats = fromMaybe (arcFormats cfg) formats,-      arcGhcOptions = fromMaybe (arcGhcOptions cfg) ovGhcOptions,-      arcNameTemplate = fromMaybe (arcNameTemplate cfg) ovNameTemplate-    }- parseFormats :: [Text] -> ConfigT [ArchiveFormat] parseFormats = fromEither "can't parse archive format" . traverse parse -withOverrides :: ReleaseArchiveOptions -> Map Name ArtifactConfig -> ConfigT [(Name, ArtifactConfig)]+withOverrides :: ReleaseArchiveOptions -> ReleaseArtifactConfigs -> ConfigT [(Name, ArtifactConfig)] withOverrides ReleaseArchiveOptions {..} cfgs = do   parsedFormats <- traverse parseFormats ovFormat-  case targetName of-    Just target -> case Map.lookup target cfgs of-      Just cfg -> pure [(target, applyOverrieds parsedFormats overrides cfg)]-      Nothing -> throwError $ fromString $ "Target \"" <> toString target <> "\" not found in configuration."-    Nothing -> pure $ map (second (applyOverrieds parsedFormats overrides)) (Map.toList cfgs)+  map (second (applyOverrieds parsedFormats)) <$> selectedArtifacts targetName cfgs+  where+    applyOverrieds formats cfg =+      cfg+        { arcFormats = fromMaybe (arcFormats cfg) formats,+          arcGhcOptions = fromMaybe (arcGhcOptions cfg) ovGhcOptions,+          arcNameTemplate = fromMaybe (arcNameTemplate cfg) ovNameTemplate+        }  runReleaseArchive :: ReleaseArchiveOptions -> ConfigT () runReleaseArchive ops@ReleaseArchiveOptions {..} = do-  prepeareDir outputDir+  prepeareDir defaultOutputDir+  cfg <- asks config   version <- asks (cfgVersion . config)   cfgs <- getArchiveConfigs >>= withOverrides ops+  ghTag <- if ghPublish then Just <$> ensureIsLatestTag version else pure Nothing+  uploadUrl <- maybe (pure Nothing) (fmap Just . getGHUploadUrl cfg) ghTag -  sectionTableM 0 "artifacts"-    $ [ ("mode", pure $ if isNothing ghPublishUrl then "local" else "publish to GitHub"),-        ("output directory", pure $ T.pack outputDir),+  sectionTableM "artifacts"+    $ [ ("destination", pure $ maybe (format outputDir) format uploadUrl),+        ("version", pure $ format version <> maybe "" (\tag -> " (GitHub Release " <> tag <> ")") ghTag),         ("targets", pure $ formatList "," (map fst cfgs))       ] -  plans <- mapMTable "build" $ map (\x -> (fst x, buildPkg x)) cfgs+  plans <- forTable "build" cfgs (\x -> (fst x, buildPkg x))    section "archive" $ pure ()   artifacts <- for plans $ \(name, plan) -> do@@ -131,15 +122,13 @@         putLine $ subPathSign <> format sha256Path     pure (name, archives) -  for_ ghPublishUrl $ \uploadUrl -> section "publish (github)"+  for_ uploadUrl $ \url -> section "publish (Github)"     $ for_ artifacts-    $ \(name, archives) -> section name-      $ for_ archives-      $ \ArchiveInfo {..} -> do-        uploadToGitHub uploadUrl archivePath-        putLine "gh published"-        uploadToGitHub uploadUrl sha256Path-        putLine "checksum published"+    $ \(name, archives) -> section name $ for_ archives $ \ArchiveInfo {..} -> do+      uploadToGitHub url archivePath+      putLine $ subPathSign <> format archivePath+      uploadToGitHub url sha256Path+      putLine $ subPathSign <> format sha256Path   where     buildPkg (name, ArtifactConfig {..}) = do       binaryDir <- genBindaryDir name
src/HWM/CLI/Command/Release/Publish.hs view
@@ -49,7 +49,7 @@ instance ParseCLI PublishOptions where   parseCLI =     PublishOptions-      <$> optional (argument str (metavar "GROUP" <> help "Name of the workspace group to publish (default: all)"))+      <$> optional (argument str (metavar "GROUP" <> help "Name of the release group to publish (default: all)"))  arrangePackageRelease :: [Pkg] -> ConfigT [Pkg] arrangePackageRelease pkgs = do@@ -72,7 +72,6 @@   version <- askVersion   when (null wgs) $ throwError "No publishable groups found. Check workspace group configuration."   sectionTableM-    0     "publish"     [ ("version", pure $ chalk Magenta (format version)),       ("target", pure $ chalk Cyan (fromMaybe "all" publishGroup)),
src/HWM/CLI/Command/Release/Root.hs view
@@ -23,8 +23,12 @@ instance ParseCLI ReleaseCommand where   parseCLI =     hsubparser-      ( command "artifacts" (info (ReleaseArchive <$> parseCLI) (progDesc "Build, archive, hash, and export a package release"))-          <> command "publish" (info (Publish <$> parseCLI) (progDesc "Publish a package release to Hackage"))+      ( command+          "artifacts"+          (info (ReleaseArchive <$> parseCLI) (progDesc "Generate, bundle, and upload release artifacts"))+          <> command+            "publish"+            (info (Publish <$> parseCLI) (progDesc "Publish the workspace packages to Hackage"))       )  runRelease :: ReleaseCommand -> ConfigT ()
src/HWM/CLI/Command/Run.hs view
@@ -25,8 +25,7 @@ import HWM.Domain.Environments (BuildEnvironment (..), getBuildEnvironment, getBuildEnvironments) import HWM.Domain.Workspace (resolveWorkspaces) import HWM.Integrations.Toolchain.Stack (createEnvYaml, stackPath)-import HWM.Runtime.Cache (prepareDir)-import HWM.Runtime.Logging (logError, logRoot)+import HWM.Runtime.Logging (logIssue) import HWM.Runtime.Process (inheritRun, silentRun) import HWM.Runtime.UI (putLine, runSpinner, sectionEnvironments, sectionWorkspace, statusIndicator) import Options.Applicative@@ -59,7 +58,6 @@  runScript :: Name -> ScriptOptions -> ConfigT () runScript scriptName ScriptOptions {..} = do-  prepareDir logRoot   cfg <- asks config   case M.lookup scriptName (cfgScripts cfg) of     Just script -> do@@ -92,7 +90,7 @@       (success, content) <- silentRun yamlPath cmd (async (runSpinner padding env))       statusIndicator padding env (statusIcon (if success then Checked else Invalid))       unless success $ do-        path <- logError buildName [("ENVIRONMENT", format benv), ("COMMAND", format cmd)] content+        path <- logIssue buildName SeverityError [("ENVIRONMENT", format benv), ("COMMAND", format cmd)] content         throwError           Issue             { issueTopic = buildName,
src/HWM/CLI/Command/Status.hs view
@@ -17,7 +17,6 @@ showStatus = do   cfg <- asks config   sectionTableM-    0     "project"     [ ("name", pure $ chalk Magenta (cfgName cfg)),       ("version", pure $ chalk Green (format $ cfgVersion cfg))
src/HWM/CLI/Command/Sync.hs view
@@ -19,13 +19,11 @@   env <- getBuildEnvironment tag   updateRegistry $ \reg -> reg {currentEnv = buildName env}   sectionTableM-    0     "sync"     [ ("enviroment", pure $ chalk Cyan $ format env),       ("resolver", pure $ buildResolver env)     ]   sectionConfig-    0     [ ("stack.yaml", syncStackYaml $> chalk Green "✓"),       ("hie.yaml", syncHie $> chalk Green "✓")     ]
src/HWM/CLI/Command/Version.hs view
@@ -20,9 +20,6 @@ import Options.Applicative.Builder (str) import Relude -size :: Int-size = 16- newtype VersionOptions = VersionOptions {bump :: Maybe VersionChange} deriving (Show)  instance ParseCLI VersionOptions where@@ -32,7 +29,6 @@ bumpVersion (BumpVersion bump) Config {..} = do   let version' = nextVersion bump cfgVersion   sectionTableM-    size     ("bump version (" <> format bump <> ")")     [ ("from", pure $ format cfgVersion),       ("to", pure $ chalk Cyan (format version'))@@ -40,7 +36,6 @@   pure Config {cfgVersion = version', cfgBounds = Nothing, ..} bumpVersion (FixedVersion version') Config {..} = do   sectionTableM-    size     "set version"     [ ("from", pure $ format cfgVersion),       ("to", pure $ chalk Cyan (format version') <> if version' == cfgVersion then " (no change)" else "")@@ -60,7 +55,7 @@  runVersion :: VersionOptions -> ConfigT () runVersion (VersionOptions (Just bump)) = (bumpVersion bump `updateConfig`) $ do-  sectionConfig size [("hwm.yaml", pure $ chalk Green "✓")]+  sectionConfig [("hwm.yaml", pure $ chalk Green "✓")]   syncPackages runVersion (VersionOptions Nothing) = do   putLine . format . cfgVersion =<< asks config
− src/HWM/CLI/Command/Workspace.hs
@@ -1,34 +0,0 @@-{-# LANGUAGE NoImplicitPrelude #-}--module HWM.CLI.Command.Workspace-  ( WorkspaceCommand (..),-    runWorkspace,-  )-where--import HWM.CLI.Command.Workspace.Add (WorkspaceAddOptions, runWorkspaceAdd)-import HWM.CLI.Command.Workspace.Ls (WorkspaceLsOptions, runWorkspaceLs)-import HWM.Core.Parsing (ParseCLI (..))-import HWM.Domain.ConfigT (ConfigT)-import Options.Applicative (command, info, progDesc, subparser)-import Relude---- | Subcommands for `hwm workspace`-data WorkspaceCommand-  = WorkspaceAdd WorkspaceAddOptions-  | WorkspaceLs WorkspaceLsOptions-  deriving (Show)--runWorkspace :: WorkspaceCommand -> ConfigT ()-runWorkspace cmd = case cmd of-  WorkspaceAdd opts -> runWorkspaceAdd opts-  WorkspaceLs opts -> runWorkspaceLs opts--instance ParseCLI WorkspaceCommand where-  parseCLI =-    subparser-      ( mconcat-          [ command "add" (info (WorkspaceAdd <$> parseCLI) (progDesc "Add a new workspace.")),-            command "ls" (info (WorkspaceLs <$> parseCLI) (progDesc "List all workspaces."))-          ]-      )
src/HWM/CLI/Command/Workspace/Add.hs view
@@ -62,7 +62,6 @@       putLine $ "• " <> chalk Bold groupId       putLine $ subPathSign <> padDots 16 memberId <> displayStatus [("added", Checked)]       sectionConfig-        0         [ ("stack.yaml", syncStackYaml $> chalk Green "✓"),           ("hie.yaml", syncHie $> chalk Green "✓")         ]
+ src/HWM/CLI/Command/Workspace/Root.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE NoImplicitPrelude #-}++module HWM.CLI.Command.Workspace.Root+  ( WorkspaceCommand (..),+    runWorkspace,+  )+where++import HWM.CLI.Command.Workspace.Add (WorkspaceAddOptions, runWorkspaceAdd)+import HWM.CLI.Command.Workspace.Ls (WorkspaceLsOptions, runWorkspaceLs)+import HWM.Core.Parsing (ParseCLI (..))+import HWM.Domain.ConfigT (ConfigT)+import Options.Applicative (command, info, progDesc, subparser)+import Relude++-- | Subcommands for `hwm workspace`+data WorkspaceCommand+  = WorkspaceAdd WorkspaceAddOptions+  | WorkspaceLs WorkspaceLsOptions+  deriving (Show)++runWorkspace :: WorkspaceCommand -> ConfigT ()+runWorkspace cmd = case cmd of+  WorkspaceAdd opts -> runWorkspaceAdd opts+  WorkspaceLs opts -> runWorkspaceLs opts++instance ParseCLI WorkspaceCommand where+  parseCLI =+    subparser+      ( mconcat+          [ command "add" (info (WorkspaceAdd <$> parseCLI) (progDesc "Add a new workspace.")),+            command "ls" (info (WorkspaceLs <$> parseCLI) (progDesc "List all workspaces."))+          ]+      )
src/HWM/Core/Options.hs view
@@ -5,6 +5,7 @@   ( Options (..),     defaultOptions,     askOptions,+    whenCI,   ) where @@ -29,3 +30,8 @@       stack = "./stack.yaml",       quiet = False     }++whenCI :: (MonadIO m) => m () -> m ()+whenCI action = do+  ci <- liftIO $ isJust <$> lookupEnv "CI"+  when ci action
src/HWM/Domain/Config.hs view
@@ -34,6 +34,7 @@  data Config = Config   { cfgName :: Name,+    cfgGithub :: Maybe Text,     cfgVersion :: Version,     cfgBounds :: Maybe Bounds,     cfgWorkspace :: Workspace,
src/HWM/Domain/Environments.hs view
@@ -49,7 +49,7 @@ import HWM.Domain.Workspace (Workspace, allPackages) import HWM.Runtime.Cache (Cache, Registry (currentEnv), VersionMap, getLatestNightlySnapshot, getRegistry, getSnapshot, getVersions) import HWM.Runtime.Files (aesonYAMLOptions, aesonYAMLOptionsAdvanced)-import HWM.Runtime.UI (MonadUI, forTable, sectionEnvironments)+import HWM.Runtime.UI (MonadUI, forTable_, sectionEnvironments) import Relude  type Extras = VersionMap@@ -245,11 +245,12 @@   active <- getBuildEnvironment name   def <- envDefault <$> askEnv   environments <- getBuildEnvironments-  sectionEnvironments (Just $ format def) $ forTable 0 environments $ \env ->+  sectionEnvironments (Just $ format def) $ forTable_ environments $ \env ->     ( format env,-      if env == active-        then chalk Cyan (buildResolver env <> " (active)")-        else chalk Gray (buildResolver env)+      pure+        $ if env == active+          then chalk Cyan (buildResolver env <> " (active)")+          else chalk Gray (buildResolver env)     )  getTestedRange :: (Monad m, MonadReader env m, Has env Workspace, Has env Environments, MonadIO m, MonadError Issue m) => m TestedRange
src/HWM/Domain/Release.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE NoImplicitPrelude #-} @@ -7,19 +8,25 @@     ArtifactConfig (..),     ArchiveFormat (..),     formatArchiveTemplate,+    ReleaseArtifactConfigs,+    getArtifact,+    selectedArtifacts,   ) where +import Control.Monad.Error.Class (MonadError (..)) import Data.Aeson   ( FromJSON (..),     ToJSON (toJSON),     genericParseJSON,     genericToJSON,   )+import qualified Data.Map as Map import Data.Yaml (Value (..)) import HWM.Core.Common (Name) import HWM.Core.Formatting (Format (..), formatTemplate) import HWM.Core.Parsing (Parse (..))+import HWM.Core.Result (Issue) import HWM.Core.Version (Version) import HWM.Domain.Workspace (WorkspaceRef) import HWM.Runtime.Files (aesonYAMLOptionsAdvanced)@@ -45,6 +52,19 @@  instance ToJSON Release where   toJSON = genericToJSON (aesonYAMLOptionsAdvanced prefix)++type ReleaseArtifactConfigs = Map Name ArtifactConfig++getArtifact :: (MonadError Issue m) => Name -> ReleaseArtifactConfigs -> m ArtifactConfig+getArtifact name cfgs = case Map.lookup name cfgs of+  Just cfg -> pure cfg+  Nothing -> throwError $ fromString $ "Artifact \"" <> toString name <> "\" not found in release configuration."++selectedArtifacts :: (MonadError Issue m) => Maybe Name -> ReleaseArtifactConfigs -> m [(Name, ArtifactConfig)]+selectedArtifacts (Just target) cfgs = do+  cfg <- getArtifact target cfgs+  pure [(target, cfg)]+selectedArtifacts Nothing cfgs = pure $ Map.toList cfgs  data ArtifactConfig = ArtifactConfig   { arcSource :: Text,
+ src/HWM/Integrations/Toolchain/Github.hs view
@@ -0,0 +1,25 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoImplicitPrelude #-}++module HWM.Integrations.Toolchain.Github (ensureIsLatestTag) where++import Control.Monad.Error.Class (MonadError (..))+import qualified Data.Text as T+import HWM.Core.Formatting (Format (format))+import HWM.Core.Result (Issue)+import HWM.Core.Version (Version)+import Relude+import System.Exit (ExitCode (..))+import System.Process (readProcessWithExitCode)++ensureIsLatestTag :: (MonadIO m, MonadError Issue m) => Version -> m Text+ensureIsLatestTag targetTag = do+  (exitCode, out, _err) <- liftIO $ readProcessWithExitCode "git" ["describe", "--tags", "--abbrev=0"] ""+  case exitCode of+    ExitSuccess -> do+      let latestTag = T.strip (T.pack out)+      if latestTag == format targetTag || latestTag == "v" <> format targetTag+        then pure latestTag+        else throwError $ fromString $ toString ("Safety abort! You are trying to upload to an older release '" <> format targetTag <> "', but your latest tag is actually '" <> latestTag <> "'.")+    ExitFailure _ -> throwError $ fromString "Safety abort! Could not find any tags in the current Git history."
src/HWM/Integrations/Toolchain/Stack.hs view
@@ -43,7 +43,7 @@ import HWM.Domain.Environments (BuildEnvironment (..), EnviromentTarget (..), Environments (..), getBuildEnvironment, hkgRefs) import HWM.Runtime.Cache (getSnapshotGHC) import HWM.Runtime.Files (aesonYAMLOptions, readYaml, rewrite_)-import HWM.Runtime.Logging (log)+import HWM.Runtime.Logging (logIssue) import HWM.Runtime.Process (exec) import Relude hiding (head, tail) import System.Directory (createDirectoryIfMissing, doesFileExist)@@ -138,7 +138,7 @@   case severity of     Nothing -> pure []     Just issueSeverity -> do-      issueFile <- log "sdist" [("COMMAND", "stack sdist " <> format (pkgName pkg)), ("SEVERITY", show issueSeverity)] (pack out)+      issueFile <- logIssue "sdist" issueSeverity [("COMMAND", "stack sdist " <> format (pkgName pkg))] (pack out)       let issueDetails = Just GenericIssue {issueFile}        in pure [Issue {..}] 
src/HWM/Runtime/Logging.hs view
@@ -2,18 +2,15 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE NoImplicitPrelude #-} -module HWM.Runtime.Logging-  ( logRoot,-    logPath,-    log,-    logError,-  )-where+module HWM.Runtime.Logging (logIssue) where  import Data.Time (getCurrentTime) import HWM.Core.Common (Name)+import HWM.Core.Options (whenCI)+import HWM.Core.Result (Severity)+import HWM.Runtime.Cache (prepareDir)+import HWM.Runtime.UI (MonadUI, putLine) import Relude-import System.Directory (createDirectoryIfMissing) import qualified System.IO as TIO  logRoot :: FilePath@@ -22,18 +19,16 @@ logPath :: Name -> FilePath logPath name = logRoot <> "/" <> toString name <> ".log" -log :: (MonadIO m) => Name -> [(Text, Text)] -> Text -> m FilePath-log name table content = do+logIssue :: (MonadIO m, MonadUI m) => Name -> Severity -> [(Text, Text)] -> Text -> m FilePath+logIssue name severity table content = do+  prepareDir logRoot   timestamp <- liftIO getCurrentTime-  let logInfo = [("TIMESTAMP", show timestamp)]+  let logInfo = [("TIMESTAMP", show timestamp), ("SEVERITY", show severity)]   let path = logPath name-  liftIO $ createDirectoryIfMissing True logRoot   let boxTop = "┌──────────────────────────────────────────────────────────"       boxBottom = "└──────────────────────────────────────────────────────────"       rows = map (\(k, v) -> "│ " <> k <> ": " <> v) (table <> logInfo)       header = unlines (boxTop : rows <> [boxBottom, "", content, ""])   liftIO $ TIO.appendFile path (toString header)+  whenCI $ putLine content   pure path--logError :: (MonadIO m) => Name -> [(Text, Text)] -> Text -> m FilePath-logError name table = log name (table <> [("TYPE", "ERROR")])
src/HWM/Runtime/Network.hs view
@@ -1,13 +1,17 @@ {-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE NoImplicitPrelude #-} -module HWM.Runtime.Network (uploadToGitHub) where+module HWM.Runtime.Network (uploadToGitHub, getGHUploadUrl) where  import Control.Monad.Except (MonadError (..))+import Data.Aeson (FromJSON) import qualified Data.Text as T import HWM.Core.Result (Issue (..))+import HWM.Domain.Config (Config (..)) import Network.HTTP.Req import Relude import System.FilePath (takeFileName)@@ -42,3 +46,46 @@                 <> header "User-Agent" "hwm-tool" -- GitHub requires a User-Agent             )       Nothing -> liftIO $ putStrLn "GitHub Upload URLs must be HTTPS"++-- 1. Define a tiny data type to represent the GitHub JSON response.+-- Aeson automatically maps the "upload_url" JSON key to this record field.++data GitHubRelease = GitHubRelease+  { name :: Text,+    upload_url :: Text+  }+  deriving (Show, Generic)++-- 2. Automatically derive the JSON parser+instance FromJSON GitHubRelease++-- 3. The main function returning the clean URL+getGHUploadUrl :: (MonadIO m, MonadError Issue m) => Config -> Text -> m Text+getGHUploadUrl Config {..} tag = do+  gh <- maybe (throwError "GitHub repository not configured") pure cfgGithub+  token <- getGitHubToken+  liftIO $ runReq defaultHttpConfig $ do+    -- Construct the endpoint URL+    let urlStr = "https://api.github.com/repos/" <> gh <> "/releases/tags/" <> tag+    uri <- liftIO $ mkURI urlStr+    case useHttpsURI uri of+      Just (url, opts) -> do+        -- Execute the GET request, expecting a JSON response matching our GitHubRelease type+        r <-+          req+            GET+            url+            NoReqBody+            jsonResponse -- This automatically parses the ByteString into our GitHubRelease data type!+            ( opts+                <> header "Authorization" ("Bearer " <> encodeUtf8 token)+                <> header "Accept" "application/vnd.github+json"+                <> header "User-Agent" "hwm-tool"+            )++        -- Extract the raw URL from the parsed JSON object+        let rawUrl = upload_url (responseBody r)++        -- Strip the "{?name,label}" template suffix before returning+        return $ T.takeWhile (/= '{') rawUrl+      Nothing -> error "GitHub API URLs must be HTTPS"
src/HWM/Runtime/UI.hs view
@@ -4,6 +4,7 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TupleSections #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE NoImplicitPrelude #-} @@ -19,12 +20,12 @@     sectionEnvironments,     sectionConfig,     sectionTableM,-    forTable,+    forTable_,     printSummary,     statusIndicator,     runSpinner,     printGenTable,-    mapMTable,+    forTable,   ) where @@ -111,32 +112,33 @@ sectionEnvironments :: (MonadUI m) => Maybe Text -> m a -> m () sectionEnvironments title = section ("environments" <> maybe "" (\name -> chalk Dim " (default: " <> chalk Magenta name <> chalk Dim ")") title) -tableM :: (MonadUI m) => Int -> [(Text, m Text)] -> m ()-tableM minSize rows = traverse_ formatRow rows-  where-    maxLabelLen = maximum (minSize : map (T.length . fst) rows) + 2-    formatRow (label, valueM) = do-      value <- valueM-      putLine $ padDots maxLabelLen label <> value--sectionTableM :: (MonadUI m) => Int -> Text -> [(Text, m Text)] -> m ()-sectionTableM size title = section title . tableM size+minRawSize :: Int+minRawSize = 16 -mapMTable :: (MonadUI m) => Text -> [(Name, m (Text, a))] -> m [(Name, a)]-mapMTable title rows = sectionBase "•" title $ traverse formatRow rows+forHLTable :: (MonadUI m) => [a] -> (a -> (Name, m (Name, b))) -> m [(Name, b)]+forHLTable as f = traverse formatRow rows   where-    maxLabelLen = maximum (0 : map (T.length . fst) rows) + 2+    rows = map f as+    maxLabelLen = maximum (minRawSize : map (T.length . fst) rows) + 2     formatRow (label, valueM) = do       (value, mvalue) <- valueM       putLine $ padDots maxLabelLen label <> value       pure (label, mvalue) -sectionConfig :: (MonadUI m) => Int -> [(Text, m Text)] -> m ()-sectionConfig size = section "config" . tableM size+forTable :: (MonadUI m) => Name -> [a] -> (a -> (Name, m (Name, b))) -> m [(Name, b)]+forTable title as f = sectionBase "•" title $ forHLTable as f -forTable :: (MonadUI m) => Int -> [a] -> (a -> (Text, Text)) -> m ()-forTable minSize rows f =-  tableM minSize (map (second pure . f) rows)+forTable_ :: (MonadUI m) => [a] -> (a -> (Text, m Text)) -> m ()+forTable_ rows f = tableM_ (map f rows)++tableM_ :: (MonadUI m) => [(Text, m Text)] -> m ()+tableM_ rows = forHLTable rows (second ((,()) <$>)) $> ()++sectionTableM :: (MonadUI m) => Text -> [(Text, m Text)] -> m ()+sectionTableM title = section title . tableM_++sectionConfig :: (MonadUI m) => [(Text, m Text)] -> m ()+sectionConfig = section "config" . tableM_  printGenTable :: (MonadUI m) => [[Text]] -> m () printGenTable rows =