packages feed

hkgr 0.3 → 0.4

raw patch · 5 files changed

+134/−76 lines, 5 files

Files

CHANGELOG.md view
@@ -1,5 +1,14 @@ # Changelog +## 0.4 (2022-03-27)+- template.cabal: use cabal 2.0 and define hs-source-dirs+- 'tagdist --existing-tag' replaces 'dist' command+- 'upload': add --existing-tag for dist rather than tagdist+- 'new': stack init with lts-17+- 'rename': experimental command to rename a project+- add '--no-hlint' option to skip running hlint+  (also hlint is no longer run for 'publish')+ ## 0.3 (2020-05-07) - only read Hackage username/password if not in ~/.cabal/config - 'upload': show newer untagged commits
README.md view
@@ -7,7 +7,7 @@ `hkgr` (pronounced "hackager") is a tool for making releases of Haskell packages on Hackage. -It uses a cautious stepped approach to releases.+It uses a cautious stepped iterative approach to releases.  ## Example usage @@ -46,8 +46,8 @@ Source tarball created: dist/hkgr-0.2.5.tar.gz ``` -Alternatively if you have manually tagged the version-with `vX.Y.Z` you can use `hkgr dist` to create a tarball.+Alternatively if you had manually tagged the release with `v0.2.5`+you can use `hkgr tagdist --existing-tag` to create a dist tarball.  The tarball can now be uploaded to Hackage as a candidate release: ```@@ -57,9 +57,14 @@ ```  One can continue to `tagdist -f` and `upload` until-everything looks good and CI passed etc,-then it is time to push the final tag and publish the release:+everything looks good and CI passed etc. +Note that `tagdist -f` and `upload`, can be combined as `upload -f`.+If one wants to check the candidate upload quickly,+it is faster just to run `upload` followed by `upload -f` if needed.++Then it is time to push the final tag and publish the release:+ ``` $ hkgr publish Everything up-to-date@@ -96,16 +101,19 @@  If sdist fails for some reason then hkgr tries to reset the tag. -### dist Alternatively if you have already manually tagged a release with 'v' prefix-you can use `hkgr dist` to create a tarball.+you can use `--existing-tag` to create a dist tarball.  ### upload `hkgr upload` uploads the tarball to Hackage as a candidate release.-Like `hkgr tagdist -f`, this can be repeated.+Like `hkgr tagdist -f`, `hkgr upload -f` can be repeated. -Haddock draft documentation can also be uploaded if desired with `hkgr upload-haddock`.+Haddock draft documentation can also be uploaded once if desired+with `hkgr upload-haddock`. +If you have an existing version tag (starting with `v`) you can use+the `--existing-tag` option to skip the tagging step (like for `tagdist`).+ ### publish `hkgr publish` releases the tarball to Hackage. @@ -127,8 +135,7 @@  A `stack.yaml` file and git repo is also set up. -One can use `hub create` etc to create the project on Github.+One can use `gh repo create` etc to create the project repo on Github.  ## Requirements- hkgr uses `cabal-install` >=2, `git`, and also `hlint` if available.
data/template.cabal.tmpl view
@@ -14,17 +14,20 @@ build-type:          Simple --  extra-doc-files:     README.md --                       ChangeLog.md-cabal-version:       1.18---tested-with:         GHC == 8.4.4, GHC == 8.6.5, GHC == 8.8.4, GHC == 8.10.4+cabal-version:       2.0+-- tested-with:         GHC == 8.6.5+--                        | == 8.8.4+--                        | == 8.10.7+--                        | == 9.0.2  source-repository head   type:                git   location:            https://github.com/@USER@/@PROJECT@.git  executable @PROJECT@-  main-is:             src/Main.hs+  main-is:             Main.hs --  other-modules:       Paths_@PROJECT_@---  hs-source-dirs:      app+  hs-source-dirs:      src   build-depends:       base < 5,                        @PROJECT@   default-language:    Haskell2010
hkgr.cabal view
@@ -1,9 +1,9 @@ cabal-version:       2.0 name:                hkgr-version:             0.3+version:             0.4 synopsis:            Simple Hackage release workflow for package maintainers description:-            Hkgr (pronounced "Hackager") is a tool to help make new releases of+            Hkgr (Hackager) is a tool to help make new releases of             Haskell packages, with commands for git tagging, pristine sdist,             and uploading to Hackage.             There is also a command for creating a new local project.@@ -11,17 +11,17 @@ bug-reports:         https://github.com/juhp/hkgr/issues license:             GPL-3 license-file:        LICENSE-author:              Jens Petersen-maintainer:          juhpetersen@gmail.com-copyright:           2019-2021  Jens Petersen+author:              Jens Petersen <juhpetersen@gmail.com>+maintainer:          Jens Petersen <juhpetersen@gmail.com>+copyright:           2019-2022  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.8.4, GHC == 8.6.5, GHC == 8.4.4, GHC == 8.2.2,-                     GHC == 8.0.2+tested-with:         GHC == 8.10.5, GHC == 8.8.4, GHC == 8.6.5, GHC == 8.4.4,+                     GHC == 8.2.2, GHC == 8.0.2  source-repository head   type:                git
src/Main.hs view
@@ -33,25 +33,39 @@     "'Hackager' is a package release tool for easy Hackage workflow" $     subcommands     [ Subcommand "new" "setup a new project" $-      newCmd <$> optional (strArg "PROJECT")+      newCmd+      <$> optional (strArg "PROJECT")     , Subcommand "tagdist" "'git tag' version and 'cabal sdist' tarball" $-      tagDistCmd False <$> forceOpt "Move existing tag"-    , Subcommand "dist" "'cabal sdist' tarball" $-      tagDistCmd True <$> forceOpt "Recreate tarball"+      tagDistCmd+      <$> existingTag+      <*> forceOpt "Move existing tag"+      <*> hlintOpt     , Subcommand "upload" "'cabal upload' candidate tarball to Hackage" $-      uploadCmd False <$> forceOpt "Move existing tag"+      uploadCmd False+      <$> existingTag+      <*> forceOpt "Move existing tag"+      <*> hlintOpt     , Subcommand "publish" "Publish to Hackage ('cabal upload --publish')" $-      pure $ uploadCmd True False+      pure $ uploadCmd True False False True     , Subcommand "upload-haddock" "Upload candidate documentation to Hackage" $       pure $ upHaddockCmd False     , Subcommand "publish-haddock" "Publish documentation to Hackage" $       pure $ upHaddockCmd True     , Subcommand "version" "Show the package version from .cabal file" $       pure showVersionCmd+    , Subcommand "rename" "Rename the Cabal package" $+      renameCmd+      <$> strArg "NEWNAME"     ]   where     forceOpt = switchWith 'f' "force" +    existingTag =+      switchWith 'T' "existing-tag" "Use existing tag to create tarball"++    hlintOpt =+      switchWith 'H' "no-hlint" "Skip running hlint"+ git :: String -> [String] -> P.ProcessConfig () () () git c args = P.proc "git" (c:args) @@ -100,26 +114,44 @@ error' = error #endif -tagDistCmd :: Bool -> Bool -> IO ()-tagDistCmd sdistOnly force = do+tarGzExt :: String+tarGzExt = "tar.gz"++assertTagOnBranch :: String -> IO ()+assertTagOnBranch tag =+    whenM (null <$> cmdOut (git "branch" ["--contains", "tags/" ++ tag])) $+    error' $ tag ++ " is no longer on branch: use --force to move it"++tagDistCmd :: Bool -> Bool -> Bool -> IO ()+tagDistCmd existingtag force noHlint = do   pkgid <- checkPackage True   let tag = pkgidTag pkgid-  tagHash <- cmdMaybe (git "rev-parse" [tag])-  if sdistOnly-    then do-    when (isNothing tagHash) $-      error' $ "tag " ++ tag ++ " do not exist"-    sdist force pkgid+  mtagHash <- cmdMaybe (git "rev-parse" [tag])+  when (not force && isJust mtagHash) $ assertTagOnBranch tag+  if existingtag+    then if isNothing mtagHash+         then error' $ "tag " ++ tag ++ " does not exist"+         else sdist force noHlint pkgid     else do-    when (isJust tagHash && not force) $-      error' $ "tag " ++ tag ++ " exists: use --force to override and move"-    git_ "tag" $ ["--force" | force] ++ [tag]-    unless force $ putStrLn tag-    sdist force pkgid `onException` do-      putStrLn "Resetting tag"-      if force-        then git_ "tag" ["--force", tag, fromJust tagHash]-        else git_ "tag" ["--delete", tag]+    if isJust mtagHash && not force+      then do+      headHash <- cmdOut (git "rev-parse" ["HEAD"])+      let onHead = Just headHash == mtagHash+      putStrLn $ "tag is " ++ (if onHead then "" else "not ") ++ "on HEAD"+      let tarball = sdistDir </> showPkgId pkgid <.> tarGzExt+      exists <- doesFileExist tarball+      if exists+        then error' $ "tag " ++ tag ++ " exists: use --force to " +++             (if exists then "override tarball and " else "") ++ "move"+        else sdist force noHlint pkgid+      else do+      git_ "tag" $ ["--force" | force] ++ [tag]+      unless force $ putStrLn tag+      sdist force noHlint pkgid `onException` do+        putStrLn "Resetting tag"+        if force+          then git_ "tag" ["--force", tag, fromJust mtagHash]+          else git_ "tag" ["--delete", tag]  pkgidTag :: PackageIdentifier -> String pkgidTag pkgid = "v" ++ packageVersion pkgid@@ -149,20 +181,20 @@      checkNotPublished :: PackageIdentifier -> IO ()     checkNotPublished pkgid = do-      let published = sdistDir </> showPkgId pkgid <.> ".tar.gz" <.> "published"+      let published = sdistDir </> showPkgId pkgid <.> tarGzExt <.> "published"       exists <- doesFileExist published       when exists $ error' $ showPkgId pkgid <> " was already published!!"-      let oldpublished = "dist" </> showPkgId pkgid <.> ".tar.gz" <.> "published"+      let oldpublished = "dist" </> showPkgId pkgid <.> tarGzExt <.> "published"       oldExists <- doesFileExist oldpublished       when oldExists $ error' $ showPkgId pkgid <> " was already published!!"  sdistDir :: FilePath sdistDir = ".hkgr" -sdist :: Bool -> PackageIdentifier -> IO ()-sdist force pkgid = do+sdist :: Bool -> Bool -> PackageIdentifier -> IO ()+sdist force noHlint pkgid = do   let tag = pkgidTag pkgid-  let target = sdistDir </> showPkgId pkgid <.> ".tar.gz"+  let target = sdistDir </> showPkgId pkgid <.> tarGzExt   haveTarget <- doesFileExist target   when haveTarget $     if force@@ -175,10 +207,11 @@     whenM (doesFileExist ".gitmodules") $       git_ "submodule" ["update", "--init"]     cabal_ "check" []-    mhlint <- findExecutable "hlint"-    when (isJust mhlint) $ do-      putStrLn "Running hlint"-      void $ P.runProcess $ P.proc "hlint" ["--no-summary", "."]+    unless noHlint $ do+      mhlint <- findExecutable "hlint"+      when (isJust mhlint) $ do+        putStrLn "Running hlint"+        void $ P.runProcess $ P.proc "hlint" ["--no-summary", "."]     let dest = takeDirectory $ cwd </> target     unlessM (doesDirectoryExist dest) $       createDirectoryIfMissing True dest@@ -190,17 +223,16 @@   putStrLn $ packageVersion pkgid  -- FIXME cabal install creates tarballs now-uploadCmd :: Bool -> Bool -> IO ()-uploadCmd publish force = do+uploadCmd :: Bool -> Bool -> Bool -> Bool -> IO ()+uploadCmd publish existingtag force noHlint = do   needProgram "cabal"   pkgid <- checkPackage False-  let file = sdistDir </> showPkgId pkgid <.> ".tar.gz"+  let tarball = sdistDir </> showPkgId pkgid <.> tarGzExt       tag = pkgidTag pkgid-  exists <- doesFileExist file+  exists <- doesFileExist tarball   when (force || not exists) $-    tagDistCmd False force-  whenM (null <$> cmdOut (git "branch" ["--contains", "tags/" ++ tag])) $-    error' $ tag ++ " is no longer on branch: use --force to move it"+    tagDistCmd existingtag force noHlint+  assertTagOnBranch tag   untagged <- cmdLines $ git "log" ["--pretty=reference", tag ++ "..HEAD"]   unless (null untagged) $ do     putStrLn "untagged newer commits:"@@ -215,10 +247,10 @@       putStrLn "done"     git_ "push" ["origin", tag]   userpassBS <- getUserPassword-  void $ P.readProcessInterleaved_ (P.setStdin (P.byteStringInput userpassBS) $ P.proc "cabal" ("upload" : ["--publish" | publish] ++ [file]))+  void $ P.readProcessInterleaved_ (P.setStdin (P.byteStringInput userpassBS) $ P.proc "cabal" ("upload" : ["--publish" | publish] ++ [tarball]))   putStrLn $ (if publish then "Published at " else "Uploaded to ") ++ "https://hackage.haskell.org/package/" ++ showPkgId pkgid ++ if publish then "" else "/candidate"   when publish $-    createFileLink (takeFileName file) (file <.> "published")+    createFileLink (takeFileName tarball) (tarball <.> "published")  getUserPassword :: IO B.ByteString getUserPassword = do@@ -298,7 +330,7 @@         else do         -- filter out dirs         dirs <- filterM doesDirectoryExist files-        if dirs == files then error' "Could not guess name"+        if dirs == files then error' "Could not guess name: subdirectories found"           else           case filter ("cabal" `isExtensionOf`) $ files \\ dirs of             [] -> takeFileName <$> getCurrentDirectory@@ -329,7 +361,7 @@   mstack <- findExecutable "stack"   -- FIXME add stack.yaml template too   when (not haveStackCfg && isJust mstack) $ do-    cmd_ "stack" ["init", "--verbosity", "warn", "--resolver", "lts-16"]+    cmd_ "stack" ["init", "--verbosity", "warn", "--resolver", "lts-19"]     sed ["/^#/d", "/^$/d"] "stack.yaml"   haveGit <- doesDirectoryExist ".git"   unless haveGit $ do@@ -344,11 +376,6 @@         [cbl] -> return $ Just (takeBaseName cbl)         _ -> error' "More than one .cabal file found!" -    sed :: [String] -> FilePath -> IO ()-    sed [] _ = error' "sed given no script"-    sed args file =-      cmd_ "sed" $ ["-i", "-e"] ++ intersperse "-e" args ++ [file]-     setupCabalTemplate :: String -> IO ()     setupCabalTemplate name = do       userTemplate <- getUserConfigFile "hkgr" "template.cabal"@@ -376,21 +403,33 @@       unlessM (doesFileExist modulePath) $ do         createDirectoryIfMissing True $ takeDirectory modulePath         writeFile modulePath "-- SPDX-License-Identifier: BSD-3-Clause\n\nmodule MyLib where\n"-        -- sed ["-i", "1s/^/-- SPDX-License-Identifier: BSD-3-Clause\n\n/"] modulePath       where-        replaceHolder lbl val file =-          sed ["s/@" ++ lbl ++ "@/" ++ val ++ "/"] file+        replaceHolder lbl val =+          sed ["s/@" ++ lbl ++ "@/" ++ val ++ "/"]          underscore '-' = '_'         underscore c = c +sed :: [String] -> FilePath -> IO ()+sed [] _ = error' "sed given no script"+sed args file =+  cmd_ "sed" $ ["-i", "-e"] ++ intersperse "-e" args ++ [file]+ #if !MIN_VERSION_filepath(1,4,2)-    isExtensionOf :: String -> FilePath -> Bool-    isExtensionOf ext@('.':_) = isSuffixOf ext . takeExtensions-    isExtensionOf ext         = isSuffixOf ('.':ext) . takeExtensions+isExtensionOf :: String -> FilePath -> Bool+isExtensionOf ext@('.':_) = isSuffixOf ext . takeExtensions+isExtensionOf ext         = isSuffixOf ('.':ext) . takeExtensions #endif  #if !MIN_VERSION_extra(1,6,15) fromMaybeM :: Monad m => m a -> m (Maybe a) -> m a fromMaybeM n = maybeM n return #endif++renameCmd :: String -> IO ()+renameCmd newname = do+  pkgid <- getPackageId+  let oldname = unPackageName (pkgName pkgid)+  sed ["s/" ++ oldname ++ "/" ++ newname ++ "/g"] $ oldname <.> "cabal"+  renameFile (oldname <.> "cabal") (newname <.> "cabal")+  -- FIXME adjust README