hackage-whatsnew (empty) → 0.1.0.0
raw patch · 5 files changed
+244/−0 lines, 5 filesdep +Cabaldep +basedep +containerssetup-changed
Dependencies added: Cabal, base, containers, directory, filepath, hackage-db, process, temporary
Files
- LICENSE +30/−0
- Main.hs +117/−0
- README.md +64/−0
- Setup.hs +2/−0
- hackage-whatsnew.cabal +31/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2015, Jeremy Shaw++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Jeremy Shaw nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Main.hs view
@@ -0,0 +1,117 @@+module Main where++import qualified Data.Map as Map+import Distribution.Hackage.DB.Parsed+import Distribution.Package (PackageName(..), PackageIdentifier(PackageIdentifier, pkgName, pkgVersion))+import Distribution.PackageDescription (GenericPackageDescription(packageDescription), PackageDescription(package))+import Distribution.PackageDescription.Parse (readPackageDescription)+import Distribution.Simple.Utils (defaultPackageDesc)+-- import Distribution.Version (Version)+import Distribution.Text (display)+import Distribution.Verbosity (Verbosity, normal)+import System.Directory ( getAppUserDataDirectory )+import System.Exit (ExitCode(..), exitSuccess, exitWith)+import System.FilePath ( (</>), (<.>) )+import System.IO (hPutStr, hPutStrLn, stderr)+import System.IO.Temp (withSystemTempDirectory)+import System.Process++-- | get the package description for the local directory+local :: Verbosity -> IO PackageDescription+local v =+ fmap packageDescription (readPackageDescription v =<< defaultPackageDesc v)++-- | get the package description from hackage+--+-- This gets the latest version available.+hackage :: PackageName -- ^ the package name+ -> IO (Maybe PackageDescription)+hackage (PackageName pn) = do+ hackage <- readHackage+ case Map.lookup pn hackage of+ Nothing ->+ return Nothing+ (Just gpdMap) ->+ return $ Just $ packageDescription (snd $ head $ Map.toDescList gpdMap)++-- | calculate the directory to the tarball for `PackageIndentifier`+packageDir :: FilePath -> PackageIdentifier -> FilePath+packageDir repoLocal (PackageIdentifier pn version) =+ repoLocal </> display pn </> display version++-- | calculate the path to the tarball for `PackageIndentifier`+packageFile :: FilePath -> PackageIdentifier -> FilePath+packageFile repoLocal pi =+ (packageDir repoLocal pi) </> display pi <.> "tar.gz"++-- | fetch `PackageIdentifier` from hackage+--+-- returns path to downloaded `.tar.gz`+fetchFromHackage :: PackageIdentifier+ -> IO FilePath+fetchFromHackage pi@(PackageIdentifier (PackageName pn) version) = do+ (ec, out, err) <- readProcessWithExitCode "cabal" ["fetch", "--no-dependencies", display pi] ""+ case ec of+ ExitSuccess -> do+ dd <- getAppUserDataDirectory "cabal"+ let repoLocal = dd </> "packages" </> "hackage.haskell.org" -- FIXME: this is the default, but users could be using a mirror or alternative repo+ return $ packageFile repoLocal pi+ (ExitFailure _) -> do+ putStrLn "Failed to fetch package from hackage."+ putStr out+ hPutStr stderr err+ exitWith (ExitFailure 2)++-- | create a `.tar.gz` in the local directory by calling `cabal sdist`.+cabalSDist :: PackageIdentifier -> IO FilePath+cabalSDist pi = do+ (ec, out, err) <- readProcessWithExitCode "cabal" ["sdist"] ""+ case ec of+ ExitFailure _ -> do+ putStr out+ hPutStr stderr err+ exitWith (ExitFailure 2)+ ExitSuccess ->+ return $ "dist" </> display pi <.> "tar.gz"++-- | calculate the `sha256sum`++sha256Sum :: FilePath -> IO String+sha256Sum fp =+ do sum <- readProcess "sha256sum" [fp] "" -- `catch`+ return $ takeWhile (/= ' ') sum++-- | extract two `tar.gz` filesa nd run a recursive diff on them.+tardiff :: FilePath -- ^ tarball 'a'+ -> FilePath -- ^ tarball 'b'+ -> IO ()+tardiff tar1 tar2 =+ withSystemTempDirectory "tardiff-XXXXXX" $ \tar1dir ->+ withSystemTempDirectory "tardiff-XXXXXX" $ \tar2dir -> do+ callProcess "tar" ["-C",tar1dir, "-x", "-z", "--strip=1", "-f", tar1]+ callProcess "tar" ["-C",tar2dir, "-x", "-z", "--strip=1", "-f", tar2]+ (ec, out, err) <- readProcessWithExitCode "diff" ["-r","-N","-u", tar1dir, tar2dir] ""+ case ec of+ ExitSuccess -> exitSuccess+ ExitFailure _ -> do+ putStr out+ hPutStr stderr err+ exitWith (ExitFailure 1)++-- | tie all the bits together+main :: IO ()+main = do+ let v = normal+ localPackage <- local v+ mHackagePackage <- hackage (pkgName $ package $ localPackage)+ case mHackagePackage of+ Nothing ->+ do hPutStrLn stderr $ "Could not find " ++ (unPackageName $ pkgName $ package $ localPackage) ++ " on hackage."+ exitWith (ExitFailure 2)+ (Just hackagePackage) -> do+ if (pkgVersion $ package localPackage) /= (pkgVersion $ package hackagePackage)+ then do hPutStrLn stderr $ "Versions do not match. Local = " ++ display (pkgVersion $ package $ localPackage) ++ ", Hackage = " ++ display (pkgVersion $ package $ hackagePackage)+ else return ()+ pf <- fetchFromHackage (package hackagePackage)+ lf <- cabalSDist (package localPackage)+ tardiff pf lf
+ README.md view
@@ -0,0 +1,64 @@+hackage-whatsnew [](https://hackage.haskell.org/package/hackage-whatsnew) [](https://travis-ci.org/stepcut/hackage-whatsnew)+================++Do you often modify a package, check that it builds in travis, and then FORGET TO UPLOAD IT!++Then this tool is for you! Its raison d'etre is to see if you have+changes in your local directory which are not on hackage.++Usage+-----++`hackage-whatsnew` depends on the following executables:++ 1. cabal++ 2. tar++ 3. GNU diff (or any `diff` which supports the `-r`, `-u`, and `-N` options)++These binaries need to be in the current search path. Assuming+everything is installed, to use `hackage-whatsnew` you simply need to:++ 1. run `cabal update`++ 2. cd into the same directory as the `.cabal` file++ 3. run `hackage-whatsnew`++If no changes are detected, then nothing is printed and the exit code is 0.++If changes are detected a recursive diff is displayed and the exit code is 1.++How It Works+------------++This tool works as follows:++ 1. read the local `.cabal` file and figure out the package name++ 2. use `cabal fetch` to get the latest version of the package from hackage++ 3. use `cabal sdist` to generate the `.tar.gz` for the local working directory++ 4. untar both `.tar.gz` bundles into temporary directories++ 5. use `diff -ruN` to check for differences++ 6. exit with 0 if no differences found++ 7. exit with 1 if differences with found++ 8. exit with 2 if other errors encountered+++FAQ+---++**Q**: Why is it called `hackage-whatsnew` instead of `hackage-diff`?++**A**: Because `hackage-diff` was already taken. The `whatsnew` term is inspired by `darcs whatsnew`.++**Q**: Would it by great if the tool did XYZ?++**A**: Yes! Please submit a pull request.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ hackage-whatsnew.cabal view
@@ -0,0 +1,31 @@+name: hackage-whatsnew+version: 0.1.0.0+synopsis: Check for differences between working directory and hackage+description: This tool checks to see if the package in a local working directory+ has changes which are not yet on hackage. This is useful to check if+ you have forgetton to upload your changes to hackage.+license: BSD3+license-file: LICENSE+author: Jeremy Shaw+maintainer: jeremy@n-heptane.com+category: Distribution+build-type: Simple+cabal-version: >=1.10+tested-with: GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.3+extra-source-files: README.md++source-repository head+ type: git+ location: https://github.com/stepcut/hackage-whatsnew.git++executable hackage-whatsnew+ main-is: Main.hs+ build-depends: base >=4.6 && <4.9,+ Cabal >= 1.16 && < 1.23,+ containers >=0.5 && <0.6,+ directory >= 1.2 && < 1.3,+ filepath >= 1.4 && < 1.5,+ hackage-db >= 1.22 && < 1.23,+ process >= 1.2 && < 1.5,+ temporary >= 1.2 && < 1.3+ default-language: Haskell2010