Skip to content

Taskfile, VS Code & CI

Practical integration patterns for projects that want dotenv-fusion to own environment resolution.

Taskfile

Use Taskfile as the project orchestrator and delegate environment work to dotenv-fusion. Keep imports, conditions, defaults and validation in .env-fuse; Taskfile should only call dotenv-fusion.

project/
├── .env-fuse
├── .env              # generated only when another tool needs it
├── configs/
│   ├── database-dev.env
│   └── database-prod.env
└── Taskfile.yml
.gitignore
.env
.env.local
.env-fuse.local
.env-fuse
ENV=${ENV:-dev}
APP_NAME=myapp

#@import configs/database-${ENV}.env

API_URL=http://localhost:8000/api

Task Dependencies

Define one task that generates .env, then make project tasks depend on it. The app, framework, Docker command or editor integration can read .env in its usual way.

Taskfile.yml
version: '3'

tasks:
  env:generate:
    desc: Generate .env
    cmds:
      - dotenv-fusion fuse -f .env-fuse {{if .ENV}}--var ENV={{.ENV}}{{end}} -o .env

  dev:
    desc: Run the app
    deps:
      - env:generate
    cmds:
      - uv run python app.py

  test:
    desc: Run tests
    deps:
      - env:generate
    cmds:
      - uv run pytest
task dev
task test
ENV=prod task dev

Leave ENV unset to use the default defined in .env-fuse. Set ENV only when a task should override it. The {{if .ENV}}...{{end}} guard avoids passing an empty --var ENV=.

dotenv-fusion fuse is already the validation point: if .env-fuse is invalid, the dependency fails and the dependent task does not run.

Prefer explicit deps on tasks that consume .env. If every task in a small Taskfile needs .env, a shared wrapper can work; mixed Taskfiles are easier to read with explicit dependencies.

If the project does not install dotenv-fusion as a dev dependency, keep the same shape and replace dotenv-fusion with uvx dotenv-fusion in the task commands.

Watch

dotenv-fusion does not have a native --watch mode. Keep watch orchestration outside the CLI for now.

With Taskfile, watch the artifact task:

Taskfile.yml
tasks:
  env:generate:
    desc: Generate .env
    sources:
      - .env-fuse
      - configs/**/*.env
    cmds:
      - dotenv-fusion fuse -f .env-fuse {{if .ENV}}--var ENV={{.ENV}}{{end}} -o .env
task --watch env:generate

Task watches the files declared in sources and reruns dotenv-fusion fuse when they change. A native dotenv-fusion --watch would be a separate CLI feature, because it would need to track source files, imports, dynamic --var values and output writes itself.

VS Code

VS Code reads a plain .env file. Use dotenv-fusion to generate that file directly; Taskfile is not required for this workflow.

project/
├── .env-fuse
├── .env              # generated, ignored by Git
├── configs/
└── .vscode/
    ├── settings.json
    ├── tasks.json
    └── launch.json

Tell VS Code and extensions to read the generated .env.

.vscode/settings.json
{
  "python.envFile": "${workspaceFolder}/.env"
}

Regenerate .env before debugging with a VS Code task that calls dotenv-fusion directly.

.vscode/tasks.json
{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "dotenv-fusion: generate .env",
      "type": "shell",
      "command": "dotenv-fusion fuse -f .env-fuse -o .env",
      "problemMatcher": []
    }
  ]
}
.vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "App",
      "type": "debugpy",
      "request": "launch",
      "program": "${workspaceFolder}/app.py",
      "envFile": "${workspaceFolder}/.env",
      "preLaunchTask": "dotenv-fusion: generate .env"
    }
  ]
}

For normal shell sessions, skip .env and load the source directly:

eval "$(dotenv-fusion load -f .env-fuse)"

CI

Use CI variables as inputs and keep the environment logic in .env-fuse. The CI job should only validate, load, or generate the resolved environment.

Validate and Build

Use this when the build command can run in a shell with exported variables.

.gitlab-ci.yml
build:
  variables:
    ENV: production
  script:
    - dotenv-fusion check -f .env-fuse
    - eval "$(dotenv-fusion load -f .env-fuse)"
    - npm run build
.github/workflows/build.yml
jobs:
  build:
    runs-on: ubuntu-latest
    env:
      ENV: production
    steps:
      - uses: actions/checkout@v4
      - run: dotenv-fusion check -f .env-fuse
      - run: |
          eval "$(dotenv-fusion load -f .env-fuse)"
          npm run build

Generate .env for Tools

For encrypted secrets, see Docker & Compose to inject values at startup with exec or provide Compose secret files.

Use this when a CI tool needs a plain dotenv file. Docker Compose accepts the default quoted dialect:

.gitlab-ci.yml
compose:build:
  variables:
    ENV: production
  script:
    - dotenv-fusion fuse -f .env-fuse -o .env
    - docker compose --env-file .env build
.github/workflows/compose.yml
jobs:
  compose:
    runs-on: ubuntu-latest
    env:
      ENV: production
    steps:
      - uses: actions/checkout@v4
      - run: dotenv-fusion fuse -f .env-fuse -o .env
      - run: docker compose --env-file .env build

docker run --env-file does not remove quotes or process escapes. Generate a separate raw artifact for that command:

dotenv-fusion fuse -f .env-fuse --dialect docker -o .env.docker
docker run --env-file .env.docker my-image

The Docker dialect rejects newline characters and NUL bytes because this line-oriented format cannot represent them.

Drift Check

Use --diff only when .env is a committed or reviewed artifact. If .env is ignored, generate it in the job instead.

.gitlab-ci.yml
env:diff:
  variables:
    ENV: production
  script:
    - dotenv-fusion fuse -f .env-fuse -o .env --diff
.github/workflows/env-diff.yml
jobs:
  env-diff:
    runs-on: ubuntu-latest
    env:
      ENV: production
    steps:
      - uses: actions/checkout@v4
      - run: dotenv-fusion fuse -f .env-fuse -o .env --diff

For branch-dependent configuration, see the branch-aware example.