mise-en-gitlab
Overview
mise-en-gitlab converts a project's mise.toml into a GitLab CI configuration fragment.
Define build/test/deploy tasks once in Mise and generate a valid .gitlab-ci.yml snippet or child-pipeline include from those definitions.
Why
- Keep your CI config DRY by treating Mise as the single source of truth for tasks.
- Generate stable, schema-checked GitLab jobs directly from your task graph.
- Make pipelines self-describing and reproducible across environments.
What gets generated
stageslist derived from each[gitlab-ci.jobs.<name>.stage]- One GitLab job per
[gitlab-ci.jobs.<name>]block - Preserves ordering and relationships (
needs,rules,artifacts, and other common keys) - Optional global defaults via
[gitlab-ci.defaults](e.g., default image) - Job key renaming via
nameunder[gitlab-ci.jobs.<name>] - Automatic
cdprefix in job scripts when a task definesdir
Installation
pip install mise-en-gitlab
Requirements: Python 3.8+.
Quick Start
Generate a GitLab CI YAML from mise.toml:
mise-en-gitlab generate --in mise.toml --out generated-ci.yml
--in: path to input mise file (default:mise.toml)--out: path to write the generated YAML (default:generated-ci.yml)-v/--verbose: show debug logs
Exit codes:
0: success1: invalid or missing CI-annotated tasks2: malformed TOML or schema error
Input: Mise tasks annotated for CI
Any task under [tasks.<name>] becomes a GitLab job when a corresponding [gitlab-ci.jobs.<name>] table is present.
Job script is taken from tasks.<name>.run. If tasks.<name>.dir is set, the first script line becomes cd <dir>.
Minimal example:
[tasks.build]
run = "pnpm build"
[gitlab-ci.jobs.build]
stage = "build"
image = "node:20"
rules = ["if: '$CI_COMMIT_BRANCH' == 'main'"]
artifacts = ["dist/"]
[tasks.test]
run = "pytest"
[gitlab-ci.jobs.test]
stage = "test"
image = "python:3.12"
[tasks.deploy]
run = "./scripts/deploy.sh"
[gitlab-ci.jobs.deploy]
stage = "deploy"
rules = ["if: '$CI_COMMIT_TAG'"]
needs = ["build", "test"]
Global defaults
You can set a default image for all jobs (unless overridden by a job) using:
[gitlab-ci.defaults]
image = "alpine:3.19"
Rename the final GitLab job key
You can rename the emitted GitLab job key using name under the job block:
[tasks.build]
run = "pnpm build"
[gitlab-ci.jobs.build]
stage = "build"
name = "build-js" # the YAML job key becomes 'build-js'
Execute a task in a specific directory
If your task has a working directory, set dir on the task; the generated script will begin with cd <dir>:
[tasks.build]
dir = "frontend"
run = ["pnpm install", "pnpm build"]
[gitlab-ci.jobs.build]
stage = "build"
image = "node:20"
The resulting script will be:
script:
- cd frontend
- pnpm install
- pnpm build
Output: GitLab CI YAML
Given the input above, the generated YAML (simplified) looks like:
stages:
- build
- test
- deploy
build:
stage: build
image: node:20
script:
- pnpm build
rules:
- if: '$CI_COMMIT_BRANCH' == 'main'
artifacts:
paths:
- dist/
test:
stage: test
image: python:3.12
script:
- pytest
deploy:
stage: deploy
script:
- ./scripts/deploy.sh
rules:
- if: '$CI_COMMIT_TAG'
needs:
- build
- test
Notes on normalization:
rulesaccepts a list of strings (e.g.,"if: <expr>") or dicts ({ if = "...", when = "..." }); both are normalized to GitLab's object form.artifactscan be a list (treated aspaths) or a table (paths,when,expire_in,reports, etc.).runcan be a string or a string list; it maps toscript(optionally prefixed bycd <dir>whentasks.<name>.diris set).- Unrecognized keys under
[gitlab-ci.jobs.<name>]are passed through as-is (common keys likebefore_script,after_script,tags,timeout,retry,interruptible,allow_failure,when,resource_group,parallel,services,variablesare supported by pass-through).
Using with Dynamic Child Pipelines
Upload the generated file as an artifact and include it in a downstream:
generate-from-mise:
stage: build
image: python:3.12
script:
- pip install mise-en-gitlab
- mise-en-gitlab generate --in mise.toml --out generated-ci.yml
artifacts:
paths:
- generated-ci.yml
trigger:
stage: test
trigger:
include:
- artifact: generated-ci.yml
job: generate-from-mise
strategy: depend
Definition of Done (internal quality bar)
When mise-en-gitlab generate runs on a valid mise.toml:
- Produces valid GitLab CI YAML with proper
stages - Outputs one job per
[tasks.*.ci]block with preserved relationships - Output is compatible with GitLab’s pipeline linter
- Works as a dynamic pipeline include without further manual edits
- Non-CI tasks are ignored
- CLI exits with appropriate codes and readable feedback for errors
Troubleshooting
- Exit code
1: No[tasks.<name>.ci]sections found. - Exit code
2: TOML parse error or schema error (e.g., missingstagein a CI-annotated task,needsnot a list of strings, missingrun). - Python 3.8–3.10 use
tomliunder the hood; Python 3.11+ usetomllib.
Development
This repo includes a mise.toml with common tasks:
# Install tool versions and generate a pre-commit hook powered by mise tasks
mise run setup-dev
# Run the full precommit pipeline (format, lint, type-check, tests)
mise precommit
Or directly with Hatch:
hatch fmt
hatch run lizard
hatch run pydoclint
hatch run pylint
hatch run typecheck
hatch run test
License
mise-en-gitlab is distributed under the terms of the MIT license.
mise-en-gitlab
Table of Contents
Installation
pip install mise-en-gitlab
License
mise-en-gitlab is distributed under the terms of the MIT license.
Release files for mise-en-gitlab 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mise_en_gitlab-0.1.1.tar.gz | 13.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mise_en_gitlab-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 24.3 kB
Release files / mise_en_gitlab-0.1.1.tar.gz
| Download URL | mise_en_gitlab-0.1.1.tar.gz |
|---|---|
| Size | 13.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1ad5a903260e6251c56da5423a60210d619086ce9abacbe4a68ed77ca050b104
|
|
BLAKE2b-256 checksum How to use checksums |
2b9dafed00a0739a77e20a0f4693bd270d72561a542ca5e11efe34ff0d14b4c3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 12, 2025.
Transparency logRelease files / mise_en_gitlab-0.1.1-py3-none-any.whl
| Download URL | mise_en_gitlab-0.1.1-py3-none-any.whl |
|---|---|
| Size | 11.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
dd8242304eb19366c7e3a47faeacda0cd6c02c69bc90a6332961c61fbb99f835
|
|
BLAKE2b-256 checksum How to use checksums |
26eedd8ca8beb095e1bb15c0a68591422c4b572ab76901541a0b7df5d2a4df2b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 12, 2025.
Transparency log