Skip to content
Open
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
1 change: 1 addition & 0 deletions cabal-install/src/Distribution/Client/Config.hs
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,7 @@ instance Semigroup SavedConfig where
{ uploadCandidate = combine uploadCandidate
, uploadDoc = combine uploadDoc
, uploadToken = combine uploadToken
, uploadTokenCmd = combine uploadTokenCmd
, uploadUsername = combine uploadUsername
, uploadPassword = combine uploadPassword
, uploadPasswordCmd = combine uploadPasswordCmd
Expand Down
14 changes: 11 additions & 3 deletions cabal-install/src/Distribution/Client/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ import Distribution.Client.Signal
( installTerminationHandler
)
import Distribution.Client.Tar (createTarGzFile)
import Distribution.Client.Types.Credentials (Password (..))
import Distribution.Client.Types.Credentials (Password (..), Token (..))
import qualified Distribution.Client.Upload as Upload
import Distribution.Client.Utils
( determineNumJobs
Expand Down Expand Up @@ -1349,6 +1349,14 @@ uploadAction uploadFlags extraArgs globalFlags = do
when (null tarfiles && not (fromFlag (uploadDoc uploadFlags'))) $
dieWithException verbosity UploadAction
checkTarFiles extraArgs
maybe_token <-
case uploadTokenCmd uploadFlags' of
Flag (xs : xss) ->
Just . Token
<$> getProgramInvocationOutput
Comment thread
LaurentRDC marked this conversation as resolved.
verbosity
(simpleProgramInvocation xs xss)
_ -> pure $ flagToMaybe $ uploadToken uploadFlags'
maybe_password <-
case uploadPasswordCmd uploadFlags' of
Flag (xs : xss) ->
Expand All @@ -1367,7 +1375,7 @@ uploadAction uploadFlags extraArgs globalFlags = do
Upload.uploadDoc
verbosity
filteredRepoContext
(flagToMaybe $ uploadToken uploadFlags')
maybe_token
(flagToMaybe $ uploadUsername uploadFlags')
maybe_password
(fromFlag (uploadCandidate uploadFlags'))
Expand All @@ -1376,7 +1384,7 @@ uploadAction uploadFlags extraArgs globalFlags = do
Upload.upload
verbosity
filteredRepoContext
(flagToMaybe $ uploadToken uploadFlags')
maybe_token
(flagToMaybe $ uploadUsername uploadFlags')
maybe_password
(fromFlag (uploadCandidate uploadFlags'))
Expand Down
16 changes: 16 additions & 0 deletions cabal-install/src/Distribution/Client/Setup.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2861,6 +2861,7 @@ data UploadFlags = UploadFlags
{ uploadCandidate :: Flag IsCandidate
, uploadDoc :: Flag Bool
, uploadToken :: Flag Token
, uploadTokenCmd :: Flag [String]
, uploadUsername :: Flag Username
, uploadPassword :: Flag Password
, uploadPasswordCmd :: Flag [String]
Expand All @@ -2876,6 +2877,7 @@ defaultUploadFlags =
{ uploadCandidate = toFlag IsCandidate
, uploadDoc = toFlag False
, uploadToken = mempty
, uploadTokenCmd = mempty
, uploadUsername = mempty
, uploadPassword = mempty
, uploadPasswordCmd = mempty
Expand Down Expand Up @@ -2929,6 +2931,20 @@ uploadCommand =
(toFlag . Token)
(flagToList . fmap unToken)
)
, option
['T']
["token-command"]
"Command to get Hackage authentication token."
uploadTokenCmd
(\v flags -> flags{uploadTokenCmd = v})
( reqArg
"COMMAND"
( readP_to_E
("Cannot parse command: " ++)
(Flag <$> parseSpaceList parseTokenQ)
)
(flagElim [] (pure . unwords . fmap show))
)
, option
['u']
["username"]
Expand Down
1 change: 1 addition & 0 deletions cabal-install/tests/IntegrationTests2.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2647,6 +2647,7 @@ testConfigOptionComments = do
"-- install-method" `assertHasCommentLine` "install-method"
"installdir" `assertHasLine` "installdir"
"-- token" `assertHasCommentLine` "token"
"-- token-command" `assertHasCommentLine` "token-command"
"-- username" `assertHasCommentLine` "username"
"-- password" `assertHasCommentLine` "password"
"-- password-command" `assertHasCommentLine` "password-command"
Expand Down
3 changes: 3 additions & 0 deletions cabal-testsuite/PackageTests/UserConfig/cabal.out
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@ Writing merged config to <ROOT>/cabal.dist/cabal-config.
# cabal user-config
Renaming <ROOT>/cabal.dist/cabal-config to <ROOT>/cabal.dist/cabal-config.backup.
Writing merged config to <ROOT>/cabal.dist/cabal-config.
# cabal user-config
Renaming <ROOT>/cabal.dist/cabal-config to <ROOT>/cabal.dist/cabal-config.backup.
Writing merged config to <ROOT>/cabal.dist/cabal-config.
4 changes: 4 additions & 0 deletions cabal-testsuite/PackageTests/UserConfig/cabal.test.hs
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@ main = cabalTest $ do
"user-config" ["update", "-f", "-a", "password-command: sh -c \"echo secret\""]
-- non-quoted tokens do get quoted when writing, but this is expected
assertFileDoesContain conf "password-command: \"sh\" \"-c\" \"echo secret\""

cabalG ["--config-file", conf]
"user-config" ["update", "-f", "-a", "token-command: sh -c \"echo secret token\""]
assertFileDoesContain conf "token-command: \"sh\" \"-c\" \"echo secret token\""
15 changes: 15 additions & 0 deletions changelog.d/token-command.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
synopsis: Add option to cabal upload to run a command to get Hackage token
packages: cabal-install
significance: significant
prs: #12132
description:{
Introduce `--token-command` option to `cabal upload`, to run a command to get a Hackage authentication token rather than providing it in plaintext.

This allows the use of a secrets manager to store Hackage tokens, like `--password-command`.

The recommended way is to add the following to your cabal config file:

```
token-command: sh -c "..."
```
}
16 changes: 16 additions & 0 deletions doc/cabal-commands.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1670,6 +1670,22 @@ to Hackage.
authentication tokens on Hackage's `account management page
<https://hackage.haskell.org/users/account-management>`__.

.. option:: -T COMMAND or -TCOMMAND, --token-command=COMMAND

Command to get your Hackage authentication token. This is useful if your
token is stored in a secrets manager, for example. Arguments with whitespace
must be quoted (double-quotes only). For example:

::

--token-command='sh -c "op read op://hackage/upload-token"'

Or in the config file:

::

token-command: sh -c "op read op://hackage/upload-token"

.. option:: -u USERNAME or -uUSERNAME, --username=USERNAME

Your Hackage username.
Expand Down
Loading