MLOps Automation
SkillDatabases & dataAutomate an MLOps project with mise tasks, lefthook hooks, Docker images, GitHub Actions, and MLflow tracking on a SQL backend. Use when adding a task runner, git hooks, CI/CD, or experiment tracking to a working package.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the MLOps Automation skill
What this skill tells your AI
The instructions your AI receives, as published by mlops-courses/mlops-coding-skills in mlops-automation/SKILL.md and read by ahel’s review.
Goal
To elevate the codebase to production standards by adding Task Automation (mise), Git Hooks (lefthook), Containerization (docker), CI/CD (github-actions), and Experiment Tracking (mlflow).
Prerequisites
- Language: Python 3.14
- Manager:
uv - Context: Preparing for scale and deployment.
Instructions
1. Task Automation
Expose a single, shared task vocabulary with mise (replaces just/make).
- Tool:
mise— pins the toolchain and defines tasks inmise.toml. - Vocabulary:
install,format,check,test,build,watch. Run everything viamise run <task>so hooks and CI reuse the same entrypoints. - Core Tasks:
format: Format code and config (ruff format,dprint fmt).check: Static checks in parallel (ruff check,ty,pip-audit,gitleaks,trivy,actionlint+zizmor).test: Runpytest.build: Build the wheel (uv build).
- The Gate: define
all = ["mise run format", "mise run check", "mise run test", "mise run build"]. This is the one command a developer, a hook, an agent, or CI runs; nothing else is allowed to define "ready". - Pinned Tools: declare every non-Python tool under
[tools], runmise lock, and commitmise.lock— it records the exact version, URL, and checksum per platform, souv.lockpins the libraries andmise.lockpins the binaries. - No Silent Installs: set
[settings.task] run_auto_install = falseso a task fails loudly on a missing tool instead of downloading one mid-run.
2. Git Hooks
Catch issues locally with lefthook (replaces pre-commit).
-
Framework:
lefthookwith thin hooks — every command delegates to amise runtask so hooks and CI stay identical. -
Explicit Priorities: lefthook orders commands alphabetically, so state the order yourself — formatters at 10, the staged secret scan at 20, the whole-tree checks at 30. Without priorities,
checkcan read files that the formatter has not rewritten yet. -
Staged Files: pass
{staged_files}to the formatters and setstage_fixed: true, so a reformat is restaged into the commit being made rather than left dirty in the working tree. -
Secret Gate: run
mise run check:leaks --stagedbefore the checks. A history scan does not look at the commit you are about to create;--stageddoes. -
pre-push: Run
mise run test. -
Example:
# lefthook.yml pre-commit: parallel: false commands: format:dprint: priority: 10 glob: "*.{json,md,toml,yaml,yml}" run: mise run format:dprint {staged_files} stage_fixed: true format:python: priority: 10 glob: "*.py" run: mise run format:python {staged_files} stage_fixed: true check:leaks: priority: 20 run: mise run check:leaks --staged check: priority: 30 run: mise run check pre-push: commands: test: run: mise run test -
Commits: Enforce Conventional Commits (e.g.,
feat: add new model) sogit-cliffcan generate the changelog.
3. Containerization
Reproducibility anywhere.
- Tool:
docker. - Base Image: Use
python:3.14-slimfor a minimal footprint; copy a pinneduvbinary into the build stage (ghcr.io/astral-sh/uv:0.12.3, never:latest— a floating tag is invisible to Dependabot). - Optimization:
- Layer Caching: Copy
uv.lock+pyproject.tomland runuv syncbefore copyingsrc/. - Multi-stage: Build inputs in one stage, copy only artifacts (the resolved
.venvordist/*.whl) to the runtime stage.
- Layer Caching: Copy
- Non-root: Run as a fixed numeric user (
USER 10001:10001) and copy with--chown=10001:10001, so ownership is stable across rebuilds and readable by a host that does not share the image's/etc/passwd. - Lint It: add
check:dockerfile = "hadolint Dockerfile"tocheck; the Dockerfile is code and deserves the same gate. - Registry: ask for the company artifact registry, or use
ghcr.iofor GitHub.
4. CI/CD Workflows
Automate verification and release with GitHub Actions.
-
Platform: ask for the company CI/CD platform, or use
github-actionsfor GitHub. -
One Step: CI runs
mise run alland nothing else. A workflow that listsformat, thencheck, thentestas separate steps will drift from the local gate; a workflow with one step cannot. -
Hardening: pin the runner (
ubuntu-24.04), settimeout-minutes, keeppermissions: contents: read, and check out withpersist-credentials: falseso no step inherits a push token it does not need. -
Prove Cleanliness: end with
test -z "$(git status --porcelain)". Becausemise run allstarts by formatting, a dirty tree at the end means a contributor committed unformatted files. -
Example:
# .github/workflows/ci.yml name: CI on: push: branches: [main] pull_request: permissions: contents: read concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} jobs: check: runs-on: ubuntu-24.04 timeout-minutes: 20 steps: - name: Checkout repository uses: actions/checkout@v7 with: persist-credentials: false # no step pushes back to the repository - name: Install mise system uses: jdx/mise-action@v4 with: cache: true # reuse the tools pinned by mise.lock - name: Run canonical gate run: mise run all - name: Verify no changes run: test -z "$(git status --porcelain)" -
Audit the Workflows: add
check:actions = ["actionlint", "zizmor --offline .github/workflows/"]tocheck.actionlintcatches invalid syntax and shell bugs;zizmorcatches workflow security issues (script injection, over-broad permissions, credential persistence). Commit.github/zizmor.ymlto record the pinning policy the project actually follows, so the audit enforces it instead of fighting it. -
Scheduled Security Pass: push CI only scans recent commits, so add
.github/workflows/security.ymlon a weeklyschedule(plusworkflow_dispatch) that checks out withfetch-depth: 0and runs the full-historygitleaks gitand a fulltrivy fs .. Keepfetch-depth: 0out ofci.yml: a full clone on every push costs time and buys nothing the scheduled job does not already cover. -
Release:
cd.ymlon Release builds the image and publishes docs via the official GitHub Pages Actions (configure-pages,upload-pages-artifact,deploy-pages). -
Dependencies: add
.github/dependabot.ymlfor every ecosystem in use (uv,github-actions,docker), grouping minor and patch updates into one pull request per ecosystem so majors are still reviewed alone.
5. AI/ML Experiments & Registry
Manage the ML lifecycle with MLflow 3.15.
- Platform:
MLflow(3.15+). - Backend: use a SQL store, locally as well as in production.
sqlite:///mlflow.dbis a real SQLAlchemy backend that supports the model registry and shares its shape with a production Postgres, so promotion is aMLFLOW_TRACKING_URIchange rather than a rewrite. The file store is deprecated: it never supported the registry, andMLFLOW_ALLOW_FILE_STORE=trueis an escape hatch to migrate off, not a configuration to ship. - Local Server:
mlflow server --backend-store-uri=sqlite:///mlflow.db --artifacts-destination=./mlartifacts. Addmlflow.dbto.gitignore— it is local state, and committing it puts a binary database in the history. - Tracking: Use
mlflow.autolog(); log metrics, params, and artifacts. - Models: Log models with the keyword
name=(e.g.,mlflow.pyfunc.log_model(name=...)). - Validation: Gate promotion with
mlflow.validate_evaluation_resultsagainst explicit metric thresholds. - Registry:
- Register top models manually or via CI.
- Aliases: Use
@championor@productionfor stable deployment pointers. Never rely on moving versions (e.g.,v1->v2).
6. Design Patterns
Write flexible code.
- Strategy: For swappable algorithms (e.g., different model types).
- Factory: For creating objects from config (e.g.,
ModelFactory). - Adapter: For standardizing mismatched interfaces.
7. Write It Down
Automation only helps if the next contributor finds it.
AGENTS.md: the commands, the definition of done, the conventions, and the layout — for AI assistants.README.md: the same ground truth for humans, without the agent-facing rules.- Rule: when a task changes, both files change in the same commit. A stale command in
AGENTS.mdis worse than no command at all.
Self-Correction Checklist
- Task Vocabulary: Does
mise run all(andformat/check/test/build) work? - Pinning: Are
mise.lockanduv.lockcommitted? - Hooks: Do the hooks use explicit priorities,
{staged_files}, andcheck:leaks --staged? - Image: Is the Dockerfile multi-stage on
python:3.14-slim, non-root, with a pinneduv, and doeshadolintpass? - CI/CD: Is
ci.ymla singlemise run allstep withpersist-credentials: falseand the porcelain assertion? - Workflow Audit: Do
actionlintandzizmorpass, and doessecurity.ymlrescan the full history weekly? - Tracking: Do runs land in a SQL-backed MLflow store, with validated thresholds before promotion?
Signals
- GitHub stars
- 22
- Forks
- 4
- Last commit
- Aug 2026
Advanced
- Catalog kind
- skill
- Gateway key
mlops-automation- Source
- github.com/mlops-courses/mlops-coding-skills