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:
- The git working tree is clean.
- The target tag does not exist locally.
- The target tag does not exist on the remote.
- The README install snippet fits the version being released. Off by default; see The README check.
mix format --check-formatted.mix test.mix docsbuilds.mix hex.buildproduces a valid package.
Then confirmation is asked, and on y:
mix.exsis rewritten with the new version.- It is committed as
Release vX.Y.Z. - An annotated tag
vX.Y.Zis created. HEADand the tag are pushed.mix hex.publishuploads 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
:satisfies— the version being released must satisfy the requirement the README names.:exact— the README must name~> MAJOR.MINORof the version being released.false— the default.:readme_path— defaults to"README.md".
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
--dry-run— run every check and build the tarball, report what would follow, write nothing--remote NAME— the git remote to push to, defaultorigin--allow-untracked— untracked files pass the clean-tree check. Tracked changes still block it; untracked files are never committed--skip CHECKS— comma-separated checks to omit:readme,format,test,docs,build--yes— skip the confirmation prompt
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