Devops
devops is a set of Mix tasks that automate the full git-flow / release /
GitHub lifecycle for a single-package Elixir project: bootstrapping a new
repository, validating and committing changes with Conventional Commit
messages, running quality gates, and cutting a tagged, changelogged,
hex-published release -- all driven by one small project config file.
It exists so that workflow (branch names, commit conventions, CI gates, release steps) lives in configuration and this shared library, not copied and adapted, script by script, into every project that needs it.
What it automates
- Bootstrap (
mix devops.init) --git init, cut the integration and production branches, generate a CI workflow, create the GitHub remote, land an initial commit, and lock branch protection to PR-only merges with required status checks. - Commit (
mix devops.commit) -- validate the change, run quality gates, stage, produce a Conventional Commit message (AI-drafted with a manual fallback), commit, push, open (or reuse) a pull request, wait on CI, and fast-forward the integration branch into production once checks pass. Every stage (local,push,merge,ff,prune) is also callable on its own, and the whole flow is resumable: a re-run after a crash, a kill, slow CI, or a network blip on push picks up exactly where it left off instead of redoing finished work or erroring out on a working tree that doesn't look "fresh". - Quality gates (
mix devops.quality,mix devops.build) -- format check, Credo, tests with 100% coverage, Dialyzer, Doctor, and (when the project looks like a Phoenix app) Sobelow, run against the project root.devops.buildadditionally fetches and compiles dependencies first;devops.build.dev/devops.build.testpinMIX_ENVregardless of what the calling shell already exports. - Version bumping (
mix devops.bump_version) -- reads HEAD's Conventional Commit type and bumps.versionaccordingly (a breaking marker escalates the bump one level), then amends the bump into HEAD. Wired to a project'spost-commitgit hook, it runs automatically after every commit. - Release (
mix devops.release) -- commit, changelog, git tag, GitHub release, andhex.publish, in that order (least to hardest to undo, since a hex publish is only reversible for one hour). Every step re-checks its own precondition, so a re-run after a stalled release picks up wherever it stopped. - Reset (
mix devops.reset --yes) -- irreversibly deletes the GitHub repo and the local.gitdirectory, undoingdevops.init. Guarded by--yesplus a second prompt that requires typing the exactowner/nameslug back.
Installation
Add devops to your project's dependencies in mix.exs:
def deps do
[
{:devops, "~> 0.6"}
]
end
Configuration
devops reads its config from the :devops application environment, using
the standard Config DSL, imported from your project's own env-specific
config file:
# config/devops.exs
import Config
config :devops, :repo, owner: "your-org", name: "your-project", visibility: :private
config :devops, :branches, production: "main"
config :devops, :commit_types, feat: :minor, fix: :patch, chore: :none
# config/config.exs (or config/dev.exs, etc.)
import Config
import_config "devops.exs"
Each section merges independently over its own defaults, so you only need to set the keys you want to change:
| Section | Key(s) | Default |
|---|---|---|
:repo | scs, owner, name, visibility | scs: :github, owner: nil, name: nil, visibility: :private |
:branches | integration, production, default, remote, tag_prefix | dev, master, master, origin, v |
:commit_types | Conventional Commit type -> version-bump effect | feat: :minor, fix: :patch, chore: :none, etc. |
:prehook | tools, commit_message, ai_backend | ["git", "mix"], Devops.CommitMessage.AI, Devops.AI.ClaudeCLI |
:posthook | bump_version | true |
:ci | await_timeout_ms, poll_interval_ms | 1_800_000, 15_000 |
:gates | a list or map of {label, args, where} gates, or nil | nil (computes Devops.Gates.default_gates/1) |
:gates is the one section that does not merge key-by-key: leave it unset to
get the built-in gate list, set it to a plain list to replace the default
outright, or set it to a map (%{label => {args, where}}) to merge over the
default list, overriding or adding individual gates without respecifying the
rest.
GITHUB_TOKEN (read by Devops.GitHub) and HEX_API_KEY (read by
Devops.HexPublish) are expected in the environment -- via a real exported
variable, or a gitignored .env/.env.#{MIX_ENV} file at the project root,
loaded automatically at boot by the vendored DotenvParser.
Usage
# One-time bootstrap of a brand-new project
$ mix devops.init
# Day to day: validate, commit, push, open a PR, wait on CI, fast-forward
$ mix devops.commit
# Just the quality gates, without fetching/compiling deps
$ mix devops.quality
# Fetch deps, compile, then run quality gates
$ mix devops.build
# Cut a release: commit, tag, changelog, GitHub release, hex.publish
$ mix devops.release
Run mix help for the full list of devops.* tasks, or see each task's own
@moduledoc for details.
Documentation
Full API documentation is generated with ExDoc and published on HexDocs.
Acknowledgements
lib/vendor/dotenv_parser.ex is vendored from
dotenv_parser by Mikko Ahlroth,
MIT licensed -- see lib/vendor/LICENSE. It is
vendored rather than taken as a regular dependency so config/runtime.exs
can load a project's .env file before any dependency's own config-time
code runs.
License
devops is released under the MIT License.