diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
 # Changelog
 
+## 0.4.3 (2023-05-30)
+- bump template to Cabal 2.2
+- add pristine 'build' command: used before publish
+- use haskeline for reading user/passwd via simple-prompt
+
 ## 0.4.2 (2022-06-24)
 - 'new': need cabal init --license option, otherwise no LICENSE file is created
 - 'github': new command to add github remote for project
@@ -93,4 +98,4 @@
 - push tag after publishing
 
 ## 0.0 (2019-06-08)
-* Initially created.
+- Initially created.
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -30,7 +30,7 @@
 []
 ["src/Main.hs","data/template.cabal.tmpl","README.md","CHANGELOG.md","LICENSE","hkgr.cabal"]
 [(NoExec,"CHANGELOG.md"),(NoExec,"LICENSE"),(NoExec,"README.md"),(NoExec,"data/template.cabal.tmpl"),(NoExec,"hkgr.cabal"),(NoExec,"src/Main.hs")]
-Wrote tarball sdist to /var/home/petersen/github/hkgr/.hkgr/hkgr-0.4.tar.gz
+Wrote tarball sdist to /home/petersen/github/hkgr/.hkgr/hkgr-0.4.tar.gz
 ```
 
 After fixing up, retag a new tarball with `--force` and upload candidate,
@@ -44,7 +44,7 @@
 []
 ["src/Main.hs","data/template.cabal.tmpl","README.md","CHANGELOG.md","LICENSE","hkgr.cabal"]
 [(NoExec,"CHANGELOG.md"),(NoExec,"LICENSE"),(NoExec,"README.md"),(NoExec,"data/template.cabal.tmpl"),(NoExec,"hkgr.cabal"),(NoExec,"src/Main.hs")]
-Wrote tarball sdist to /var/home/petersen/github/hkgr/.hkgr/hkgr-0.4.tar.gz
+Wrote tarball sdist to /home/petersen/github/hkgr/.hkgr/hkgr-0.4.tar.gz
 hackage.haskell.org password:
 Uploaded to https://hackage.haskell.org/package/hkgr-0.4/candidate
 ```
@@ -71,11 +71,12 @@
 
 ```shellsession
 $ hkgr --version
-0.4.2
+0.4.3
 $ hkgr --help
 Hackage Release tool
 
 Usage: hkgr [--version] COMMAND
+
   'Hackager' is a package release tool for easy Hackage workflow
 
 Available options:
@@ -89,6 +90,7 @@
   publish                  Publish to Hackage ('cabal upload --publish')
   upload-haddock           Upload candidate documentation to Hackage
   publish-haddock          Publish documentation to Hackage
+  build                    Do a local pristine build from the tarball
   version                  Show the package version from .cabal file
   rename                   Rename the Cabal package
   github                   Add github repo
@@ -134,14 +136,19 @@
 the `--existing-tag` option to skip the tagging step (like for `tagdist`).
 
 ### publish
-`hkgr publish` releases the tarball to Hackage.
+`hkgr publish` releases the tarball to Hackage, after doing a pristine
+local build and git pushing the tag and its commits to origin.
 
-If it succeeds then hkgr creates a "published lockfile" in `dist/`,
-and the git tag is pushed to origin.
+If it succeeds, then hkgr creates a "published lockfile" in `.hkgr/`.
 
 (Then hkgr will refuse to do further commands on the released version.)
 
 Optionally one can publish haddock docs with `hkgr publish-haddock`.
+
+### build
+`hkgr build` will try to do a pristine build of the latest created tarball
+for the tag. This is useful for catching missing files from the tarball,
+preventing brownbag releases.
 
 ### new
 `hkgr new` creates a new project.
diff --git a/data/template.cabal.tmpl b/data/template.cabal.tmpl
--- a/data/template.cabal.tmpl
+++ b/data/template.cabal.tmpl
@@ -3,7 +3,7 @@
 synopsis:            One line summary
 description:
         Short paragraph here
-license:             BSD3
+license:             BSD-3-clause
 license-file:        LICENSE
 author:              @NAME@ <@EMAIL@>
 maintainer:          @NAME@ <@EMAIL@>
@@ -14,11 +14,12 @@
 build-type:          Simple
 --  extra-doc-files:     README.md
 --                       ChangeLog.md
-cabal-version:       2.0
--- tested-with:         GHC == 8.6.5
---                       || == 8.8.4
+cabal-version:       2.2
+-- tested-with:         GHC == 8.8.4
 --                       || == 8.10.7
 --                       || == 9.0.2
+--                       || == 9.2.7
+--                       || == 9.4.5
 
 source-repository head
   type:                git
diff --git a/hkgr.cabal b/hkgr.cabal
--- a/hkgr.cabal
+++ b/hkgr.cabal
@@ -1,6 +1,6 @@
 cabal-version:       2.0
 name:                hkgr
-version:             0.4.2
+version:             0.4.3
 synopsis:            Simple Hackage release workflow for package maintainers
 description:
             Hkgr (Hackager) is a tool to help make new releases of
@@ -13,15 +13,15 @@
 license-file:        LICENSE
 author:              Jens Petersen <juhpetersen@gmail.com>
 maintainer:          Jens Petersen <juhpetersen@gmail.com>
-copyright:           2019-2022  Jens Petersen
+copyright:           2019-2023  Jens Petersen
 category:            Util
 build-type:          Simple
 data-dir:            data
 data-files:          template.cabal.tmpl
 extra-doc-files:     README.md
                    , CHANGELOG.md
-tested-with:         GHC == 8.10.7, GHC == 8.8.4, GHC == 8.6.5, GHC == 8.4.4,
-                     GHC == 8.2.2, GHC == 8.0.2
+tested-with:         GHC == 9.6 || == 9.4 || == 9.2 || == 9.0 || == 8.10
+                     || == 8.8 || == 8.6 || == 8.4 || == 8.2 || == 8.0
 
 source-repository head
   type:                git
@@ -39,6 +39,7 @@
                      , filepath
                      , simple-cabal >= 0.1.0
                      , simple-cmd-args >= 0.1.6
+                     , simple-prompt >= 0.2
                      , typed-process
                      , xdg-basedir
 
diff --git a/src/Main.hs b/src/Main.hs
--- a/src/Main.hs
+++ b/src/Main.hs
@@ -6,7 +6,7 @@
 import Control.Applicative (pure, (<$>))
 #endif
 
-import Control.Exception (bracket_, finally, onException)
+import Control.Exception (bracket_, onException)
 import Control.Monad.Extra
 import qualified Data.ByteString.Lazy.Char8 as B
 import Data.Char
@@ -18,12 +18,12 @@
 import Data.Version.Extra
 import SimpleCabal
 import SimpleCmdArgs
+import SimplePrompt (promptNonEmpty, promptPassword)
 import System.Directory
 import System.Environment.XDG.BaseDir
 import System.Exit (ExitCode (..))
 import System.FilePath
-import System.IO (BufferMode(NoBuffering), hSetBuffering, hSetEcho,
-                  stdin, stdout)
+import System.IO (BufferMode(NoBuffering), hSetBuffering, stdout)
 import qualified System.Process.Typed as P
 import Paths_hkgr (getDataFileName, version)
 
@@ -46,12 +46,16 @@
       <$> existingTag
       <*> forceOpt "Move existing tag"
       <*> hlintOpt
+      <*> pure False
     , Subcommand "publish" "Publish to Hackage ('cabal upload --publish')" $
-      pure $ uploadCmd True False False True
+      uploadCmd True False False True
+      <$> switchWith 'N' "no-build" "Do not perhaps a local sanity build"
     , Subcommand "upload-haddock" "Upload candidate documentation to Hackage" $
       pure $ upHaddockCmd False
     , Subcommand "publish-haddock" "Publish documentation to Hackage" $
       pure $ upHaddockCmd True
+    , Subcommand "build" "Do a local pristine build from the tarball" $
+      pure pristineBuildCmd
     , Subcommand "version" "Show the package version from .cabal file" $
       pure showVersionCmd
     , Subcommand "rename" "Rename the Cabal package" $
@@ -226,8 +230,8 @@
   putStrLn $ packageVersion pkgid
 
 -- FIXME cabal install creates tarballs now
-uploadCmd :: Bool -> Bool -> Bool -> Bool -> IO ()
-uploadCmd publish existingtag force noHlint = do
+uploadCmd :: Bool -> Bool -> Bool -> Bool -> Bool -> IO ()
+uploadCmd publish existingtag force noHlint nobuild = do
   needProgram "cabal"
   pkgid <- checkPackage False
   let tarball = sdistDir </> showPkgId pkgid <.> tarGzExt
@@ -238,9 +242,11 @@
   assertTagOnBranch tag
   untagged <- cmdLines $ git "log" ["--pretty=reference", tag ++ "..HEAD"]
   unless (null untagged) $ do
-    putStrLn "untagged newer commits:"
+    putStrLn $ "untagged newer commit" ++
+      (if length untagged > 1 then "s" else "") ++ ":"
     mapM_ putStrLn untagged
   when publish $ do
+    unless nobuild pristineBuildCmd
     tagHash <- cmdOut $ git "rev-parse" [tag]
     branch <- cmdOut $ git "branch" ["--show-current"]
     mergeable <- gitBool "merge-base" ["--is-ancestor", "HEAD", tagHash]
@@ -282,16 +288,9 @@
 
 prompt :: Bool -> String -> IO String
 prompt hide s = do
-  putStr $ s ++ ": "
-  inp <- if hide then withoutEcho getLine else getLine
+  inp <- (if hide then promptPassword else promptNonEmpty) s
   when hide $ putChar '\n'
-  if null inp
-    then prompt hide s
-    else return inp
-  where
-    withoutEcho :: IO a -> IO a
-    withoutEcho action =
-      finally (hSetEcho stdin False >> action) (hSetEcho stdin True)
+  return inp
 
 upHaddockCmd :: Bool -> IO ()
 upHaddockCmd publish = do
@@ -433,6 +432,7 @@
 fromMaybeM n = maybeM n return
 #endif
 
+-- FIXME rename github remote too?
 renameCmd :: String -> IO ()
 renameCmd newname = do
   pkgid <- getPackageId
@@ -453,3 +453,17 @@
   git_ "remote" ["add", "origin", "git@github.com:" ++ ghuser </> name <.> "git"]
   git_ "branch" ["-M", "main"]
 --  git_ "push" ["-u", "origin", "main"]
+
+pristineBuildCmd :: IO ()
+pristineBuildCmd = do
+  needProgram "cabal"
+  pkgid <- getPackageId
+  cwd <- getCurrentDirectory
+  let tarball = cwd </> sdistDir </> showPkgId pkgid <.> tarGzExt
+  exists <- doesFileExist tarball
+  unless exists $
+    error' $ "Please 'tagdist' first: " ++ tarball ++ " not found"
+  withTempDirectory "tmp-build" $ do
+    cmd_ "tar" ["xf", tarball]
+    setCurrentDirectory $ showPkgId pkgid
+    cabal_ "build" []
