diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,9 @@
 # Changelog
 
-## 0.0
+## 0.1 (2019-06-??)
+- add published lock file: prevents tagging/dist/upload after publish
+- tag before sdist if no tag
+- push tag after publishing
+
+## 0.0 (2019-06-08)
 * Initially created.
diff --git a/Main.hs b/Main.hs
--- a/Main.hs
+++ b/Main.hs
@@ -13,6 +13,7 @@
 #else
 import Data.Semigroup ((<>))
 #endif
+import SimpleCabal
 import SimpleCmd
 import SimpleCmd.Git
 import SimpleCmdArgs
@@ -32,46 +33,35 @@
   , Subcommand "version" "Show the package version from .cabal file" $
     pure showVersionCmd
   , Subcommand "upload" "'cabal upload' tarball to Hackage" $ pure $ uploadCmd False
-  , Subcommand "push-tags" "'git push --tags' to origin" $ pure pushCmd
   , Subcommand "publish" "Publish to Hackage ('cabal upload --publish')" $
     pure $ uploadCmd True
   , Subcommand "upload-haddock" "Upload documentation to Hackage" $ pure $ upHaddockCmd False
   , Subcommand "publish-haddock" "Upload documentation to Hackage" $ pure $ upHaddockCmd True
+  , Subcommand "push-tags" "'git push --tags' to origin" $ pure pushCmd
   ]
   where
     forceOpt = switchWith 'f' "force"
 
-getName :: IO String
-getName =
-  takeBaseName <$> getCurrentDirectory
-
-getCabalFile :: IO FilePath
-getCabalFile = do
-  name <- getName
-  return $ name <.> "cabal"
-
-getVersion :: IO String
-getVersion = do
-  cbl <- getCabalFile
-  cmd "sed" ["-ne", "s/^[Vv]ersion:[[:space:]]*//p", cbl]
-
-getTarfile :: IO FilePath
-getTarfile = do
-  name <- getName
-  ver <- getVersion
-  return $ "dist" </> name <> "-" <> ver <.> ".tar.gz"
-
 gitTagCmd :: Bool -> IO ()
 gitTagCmd force = do
-  ver <- getVersion
-  git_ "tag" $ ["--force" | force] ++ [ver]
+  pkgid <- getPackageId
+  checkNotPublished pkgid
+  let tag = packageVersion pkgid
+  git_ "tag" $ ["--force" | force] ++ [tag]
+  unless force $ putStrLn tag
 
+checkNotPublished :: PackageIdentifier -> IO ()
+checkNotPublished pkgid = do
+  let published = "dist" </> showPkgId pkgid <.> ".tar.gz" <.> "published"
+  exists <- doesFileExist published
+  when exists $ error' $ showPkgId pkgid <> " was already published!!"
+
 sdistCmd :: Bool -> IO ()
 sdistCmd force = do
-  ver <- getVersion
-  name <- getName
-  let filename = name <> "-" <> ver <.> ".tar.gz"
-      target = "dist" </> filename
+  pkgid <- getPackageId
+  let ver = packageVersion pkgid
+  let target = "dist" </> showPkgId pkgid <.> ".tar.gz"
+  checkNotPublished pkgid
   haveTarget <- doesFileExist target
   if haveTarget
     then if force
@@ -79,7 +69,7 @@
          else error' $ target <> " exists already!"
     else when force $ error' "Target does not exist, please use 'dist' command"
   haveTag <- pipeBool ("git", ["tag"]) ("grep",["-q",ver])
-  unless haveTag $ error' $ "Please tag " <> ver <> " first!"
+  unless haveTag $ gitTagCmd False
   cwd <- getCurrentDirectory
   withTempDirectory "tmp-sdist" $ do
     git_ "clone" ["-q", "--no-checkout", "..", "."]
@@ -93,13 +83,19 @@
 
 showVersionCmd :: IO ()
 showVersionCmd = do
-  ver <- getVersion
-  putStrLn ver
+  pkgid <- getPackageId
+  putStrLn $ packageVersion pkgid
 
 uploadCmd :: Bool -> IO ()
 uploadCmd publish = do
-  file <- getTarfile
+  pkgid <- getPackageId
+  checkNotPublished pkgid
+  let file = "dist" </> showPkgId pkgid <.> ".tar.gz"
   cabal_ "upload" $ ["--publish" | publish] ++ [file]
+  when publish $ do
+    let tag = packageVersion pkgid
+    git_ "push" ["origin", tag]
+    createFileLink file (takeFileName file <.> "published")
 
 pushCmd :: IO ()
 pushCmd =
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -4,8 +4,8 @@
 [![GPL-3 license](https://img.shields.io/badge/license-GPL--3-blue.svg)](LICENSE)
 [![Build status](https://secure.travis-ci.org/juhp/hkgr.svg)](https://travis-ci.org/juhp/hkgr)
 
-`hkgr` (pronounced "Hackager") is a tool for package maintainers
-to release their Haskell packages on Hackage.
+`hkgr` (pronounced "Hackager") is a tool for making releases of
+Haskell packages on Hackage.
 
 ## Usage
 
@@ -25,21 +25,20 @@
   dist                     Make tarball from latest tag ('cabal sdist')
   version                  Show the package version from .cabal file
   upload                   'cabal upload' tarball to Hackage
-  push-tags                'git push --tags' to origin
   publish                  Publish to Hackage ('cabal upload --publish')
   upload-haddock           Upload documentation to Hackage
   publish-haddock          Upload documentation to Hackage
+  push-tags                'git push --tags' to origin
 ```
 
 ## Example
 ```
 $ git commit -m "new release"
+$ git push
 $ hkgr tag
 $ hkgr dist
 $ hkgr upload
-$ git push
-$ hkgr push-tags
-$ hkgr publish
 $ hkgr upload-haddock
+$ hkgr publish
 $ hkgr publish-haddock
 ```
diff --git a/hkgr.cabal b/hkgr.cabal
--- a/hkgr.cabal
+++ b/hkgr.cabal
@@ -1,11 +1,11 @@
 cabal-version:       2.0
 name:                hkgr
-version:             0.0
+version:             0.1
 synopsis:            Simple Hackage release workflow for package maintainers
 description:
-            Hkgr (pronouced "Hackager") is a tool to help Hackage maintainers
-            make new releases of their packages, with commands for git tagging,
-            pristine sdist, and uploading to Hackage.
+            Hkgr (pronouced "Hackager") is a tool to help make new releases of
+            Haskell packages, with commands for git tagging, pristine sdist,
+            and uploading to Hackage.
 homepage:            https://github.com/juhp/hkgr
 bug-reports:         https://github.com/juhp/hkgr/issues
 license:             GPL-3
@@ -29,14 +29,15 @@
   autogen-modules:     Paths_hkgr
 
   build-depends:       base < 5,
-                       directory >= 1.2.3,
+                       directory >= 1.3.1.0,
                        filepath,
+                       simple-cabal,
                        simple-cmd >= 0.2.0,
                        simple-cmd-args
   if impl(ghc<8.0)
       build-depends: semigroups
 
-  ghc-options:         -Wall -fwarn-missing-signatures
+  ghc-options:         -Wall
 
   default-language:    Haskell2010
   default-extensions:  OverloadedStrings
