Plumb

A mix publish task that releases a package to Hex.

Every check runs before anything is written. A run that fails verification leaves the repository unchanged.

mix publish patch # 0.1.1 -> 0.1.2
mix publish minor # 0.1.1 -> 0.2.0
mix publish major # 0.1.1 -> 1.0.0
mix publish 0.4.0-rc.1 # an explicit version, which must sort above the current one
mix publish current # upload the version already in mix.exs

Installation

def deps do
[
{:plumb, "~> 0.2", only: :dev, runtime: false}
]
end

Sequence

Checks, in order. Nothing is written until all of them pass:

  1. The git working tree is clean.
  2. The target tag does not exist locally.
  3. The target tag does not exist on the remote.
  4. The README install snippet fits the version being released. Off by default; see The README check.
  5. mix format --check-formatted.
  6. mix test.
  7. mix docs builds.
  8. mix hex.build produces a valid package.

Then confirmation is asked, and on y:

  1. mix.exs is rewritten with the new version.
  2. It is committed as Release vX.Y.Z.
  3. An annotated tag vX.Y.Z is created.
  4. HEAD and the tag are pushed.
  5. mix hex.publish uploads the package and its documentation.

The tag is pushed before the upload, so source_ref in the published documentation resolves as soon as the docs are live.

mix publish current

Publishes the version already in mix.exs, without bumping it. This is the first release of a package, and the second attempt at a release whose upload did not happen.

Where the tag already exists it must be on the remote, point at the same commit in both, and have HEAD on it — the tarball is built from the working tree, so a HEAD ahead of the tag would upload code the tag does not point at, and is refused. Where no tag exists, HEAD is tagged and pushed before the upload.

mix.exs is never rewritten, so the version you publish is the one you wrote.

The README check

Compares the requirement the README names for this package against the version being released — not the version in mix.exs — so it fails before the bump.

Off unless configured:

def project do
[
plumb: [readme: :exact]
]
end

The requirement is read from the first {:app_name, "requirement"} in the file. A README that names no dependency on the package fails the check.

Where the version comes from

A @version "..." module attribute, when there is one:

@version "0.3.0"
def project do
[version: @version, docs: [source_ref: "v#{@version}"]]
end

Otherwise a literal version: "0.3.0" in the project config. Only the first occurrence is rewritten, and only when the version is a literal string — a version: @version is left alone so the attribute is what gets bumped.

A mix.exs with neither is refused.

Options

mix publish minor --dry-run
mix publish patch --remote upstream
mix publish patch --skip docs

Scope

Changelog entries are not generated. expublish covers similar ground and does generate them.

License

MIT