Diffusion
Polar Diffusion
Getting Started
Overview Prerequisites Installation Quick Start
Commands
molecule role deps cache artifact show
Configuration
diffusion.toml Registries Project Structure CI/CD
Guides
Dependency Management Cache Feature Artifact Management Building from Source Verification Unix Permissions Role Version Constraints Migration Guide
Ansible Role Testing Framework

Diffusion CLI

Simplify Ansible role testing with Molecule — cross-platform, Docker-based, with dependency locking, multi-registry auth, and Vault integration.

Overview

Diffusion is the CLI tool for testing Ansible roles with Molecule. It manages the full testing lifecycle — converge, verify, lint, idempotence — inside a single Docker container, with built-in registry auth, HashiCorp Vault integration, and dependency locking.

🚀

Role Management

Initialize and configure Ansible roles interactively. Add dependencies, collections, and scenarios with a single command.

🐳

Docker-Based Testing

Full Molecule lifecycle — converge, verify, lint, idempotence — managed inside a single container. No manual setup needed.

🌐

Multi-Registry Support

Auto-authenticated access to Public, Yandex Cloud, AWS ECR, and GCP Artifact Registry out of the box.

🔐

Vault Integration

Secure credential management via HashiCorp Vault KV2 or local AES-256-GCM encrypted storage per artifact source.

🔒

Dependency Lock Files

Pin Python, Ansible, Molecule, and collection versions to diffusion.lock for fully reproducible builds.

Caching

Cache Ansible roles, collections, Docker images, and UV/Python packages between runs for 3–10× faster iteration.

🔄

CI/CD Ready

The --ci flag clones the repo inside the container, removes TTY requirements, and disables spinners for clean pipeline logs.

🔑

Artifact Management

Manage credentials for multiple private repositories with encrypted local storage or Vault — indexed as GIT_USER_N env vars.

Prerequisites

Required

  • Docker containerized testing
  • Go 1.25.4+ only if building from source

Optional CLIs

  • Vault CLI HashiCorp Vault integration
  • YC CLI Yandex Cloud registry
  • AWS CLI AWS ECR registry
  • gcloud CLI GCP registry

Recommended Terminal

  • WezTerm or Ghostty for best Unicode/color support
  • Nerd Fonts: FiraCode, JetBrains Mono, Hack

Installation

# Install
choco install diffusion

# Upgrade
choco upgrade diffusion

# Optional: enable signature and provenance verification
choco install cosign

Auto-detects architecture (AMD64 / ARM64 / ARM), verifies SHA256, adds diffusion to PATH.

go install github.com/Polar-Team/diffusion@latest
git clone https://github.com/Polar-Team/diffusion.git
cd diffusion
make build    # binary -> bin/
make dist     # Linux, macOS, Windows x AMD64/ARM64/ARM
make linux    # Linux only
make darwin   # macOS only
make windows  # Windows only

Output binaries land in bin/ with descriptive names like diffusion-linux-amd64.

Quick Start

# 1. Scaffold a new Ansible role
diffusion role --init

# 2. Initialize and lock dependencies
diffusion deps init
diffusion deps lock

# 3. Run the full test cycle
diffusion molecule             # converge
diffusion molecule --verify
diffusion molecule --lint
diffusion molecule --idempotence
diffusion molecule --destroy

diffusion molecule

Runs the Molecule testing workflow. Role name and org are auto-detected from meta/main.yml.

FlagDescription
diffusion moleculeRun converge (default)
--converge [--tag "t1,t2"]Apply role with optional Ansible tags
--verify [--tag "check"]Run verify tests
--lintRun yamllint + ansible-lint
--idempotence [--tag "t"]Idempotence check
--destroyDestroy test instances
--wipeRemove container + molecule folder
--ciCI/CD mode no TTY, no spinners, clones repo inside container
--role / --orgOverride auto-detected role/org
--testsoverwriteOverwrite molecule tests folder
Test flags (--converge, --verify, --lint, --idempotence, --destroy) are mutually exclusive only one at a time.

Typical workflow

diffusion molecule --converge
diffusion molecule --verify
diffusion molecule --lint
diffusion molecule --idempotence
diffusion molecule --destroy
diffusion molecule --wipe

CI/CD mode

The --ci flag clones the repository inside the container instead of using volume mounts, eliminating TTY and permission issues in CI runners.

# GitHub Actions
- run: diffusion molecule --ci --converge
- run: diffusion molecule --ci --verify

# GitLab CI
script:
  - diffusion molecule --ci --converge
  - diffusion molecule --ci --verify

diffusion role

CommandDescription
diffusion roleView current role configuration
diffusion role --initInitialize a new role interactively
diffusion role add-role <name> --src <url> --version mainAdd a role dependency
diffusion role remove-role <name>Remove a role dependency
diffusion role add-collection community.generalAdd a collection
diffusion role remove-collection community.generalRemove a collection
--scenario / -s <name>Target a specific Molecule scenario (default: default)
Without --init, the command displays current config. Use --init to scaffold a new role. If a role already exists, --init will warn you.

diffusion deps

CommandDescription
diffusion deps initAdd [dependencies] section to diffusion.toml
diffusion deps lockResolve versions from PyPI/Galaxy and write diffusion.lock
diffusion deps checkVerify lock file is up-to-date (exits 1 if not ideal for CI)
diffusion deps resolvePretty-print all resolved versions from lock file
diffusion deps syncWrite locked versions back to requirements.yml / meta.yml

diffusion cache

CommandDescription
diffusion cache enableEnable caching for current role
diffusion cache enable --dockerAlso cache Docker images as tarballs
diffusion cache enable --uvAlso cache UV/Python packages
diffusion cache disableDisable caching (preserves cache directory)
diffusion cache cleanRemove cached artifacts for current role
diffusion cache statusShow cache config and size
diffusion cache listList all cache directories across all roles

diffusion artifact

CommandDescription
diffusion artifact add <name>Store encrypted credentials for a private repo
diffusion artifact listList all stored artifact sources
diffusion artifact show <name>Show source details (token masked)
diffusion artifact remove <name>Remove stored credentials and config entry

Credentials are encrypted with AES-256-GCM using a machine-specific key derived from hostname:username. Stored in ~/.diffusion/secrets/<role>/<source> with 0700 directory permissions.

diffusion show

Displays all Diffusion configuration in a readable format.

diffusion show

⚙️ diffusion.toml

Created interactively on first run in your project directory. Run diffusion show to view current config.

[container_registry]
registry_server = "ghcr.io"
registry_provider = "Public"
molecule_container_name = "polar-team/diffusion-molecule-container"
molecule_container_tag = "latest-amd64"

[vault]
enabled = false

[yaml_lint]
extends = "default"
ignore = [".git/*", "molecule/**", "vars/*", "files/*"]

[yaml_lint.rules]
braces = { max-spaces-inside = 1, level = "warning" }
brackets = { max-spaces-inside = 1, level = "warning" }
comments = { min-spaces-from-content = 1 }
comments-indentation = false
octal-values = { forbid-implicit-octal = true }

[ansible_lint]
exclude_paths = ["molecule/default/tests/*.yml", "tests/test.yml"]
warn_list = ["meta-no-info", "yaml[line-length]"]
skip_list = ["meta-incorrect", "role-name[path]"]

[tests]
type = "local"

[cache]
enabled = false

[dependencies]
ansible = ">=10.0.0"
molecule = ">=24.0.0"
ansible_lint = ">=24.0.0"
yamllint = ">=1.35.0"

[dependencies.python]
min = "3.11"
max = "3.13"
pinned = "3.13"
[container_registry]
registry_server = "cr.yandex"
registry_provider = "YC"
molecule_container_name = "crp1234567890/diffusion-molecule-container"
molecule_container_tag = "latest-amd64"

[vault]
enabled = true

[[artifact_sources]]
name = "gitlab-private"
url = "https://gitlab.company.com"
use_vault = true
vault_path = "secret/data/artifacts"
vault_secret_name = "gitlab-creds"
vault_username_field = "username"
vault_token_field = "token"

[tests]
type = "diffusion"

[cache]
enabled = true
[container_registry]
registry_server = "us-docker.pkg.dev"
registry_provider = "GCP"
molecule_container_name = "my-project/my-repo/diffusion-molecule-container"
molecule_container_tag = "latest-amd64"

[vault]
enabled = false

[tests]
type = "local"

[cache]
enabled = false

Configuration reference

Container Registry

  • registry_server — e.g. ghcr.io, cr.yandex
  • registry_providerPublic | YC | AWS | GCP
  • molecule_container_name — image path
  • molecule_container_tag — auto-detected arch tag

Tests Type

  • local — copy from project tests/
  • remote — clone from Git repos via remote_repositories
  • diffusion — official diffusion-ansible-tests-role

Dependencies

  • Version constraints for ansible, molecule, ansible_lint, yamllint
  • Python min / max / pinned (allowed: 3.11, 3.12, 3.13)
  • Collections: [[dependencies.collections]]
  • Roles: [[dependencies.roles]]

Vault

  • enabled = true to activate
  • Requires VAULT_ADDR + VAULT_TOKEN env vars
  • Pass to test containers via molecule.yml env block

Passing credentials to test containers

Pass environment variables to test containers inside the Diffusion container via molecule.yml:

# molecule/default/molecule.yml
platforms:
  - name: instance
    image: your-registry/test-image:latest
    env:
      TOKEN: "${TOKEN}"              # Registry token (YC, AWS, GCP)
      VAULT_ADDR: "${VAULT_ADDR}"    # Vault address
      VAULT_TOKEN: "${VAULT_TOKEN}"  # Vault token

🌐 Registry Support

ProviderRegistry FormatAuth CommandCLI Required
Publicghcr.io, docker.ioNoneNone
Yandex Cloudcr.yandexyc iam create-tokenYC CLI
AWS ECR<account>.dkr.ecr.<region>.amazonaws.comaws ecr get-login-passwordAWS CLI
GCP Container Registrygcr.io, us.gcr.iogcloud auth print-access-tokengcloud CLI
GCP Artifact Registry<region>-docker.pkg.devgcloud auth print-access-tokengcloud CLI
Ensure the respective CLI tool is installed, authenticated, and configured before running Diffusion. Authentication tokens are automatically retrieved and used for Docker login.

GCP configuration examples

[container_registry]
registry_server = "us-docker.pkg.dev"
registry_provider = "GCP"
molecule_container_name = "my-project/my-repo/diffusion-molecule-container"
molecule_container_tag = "latest-amd64"
[container_registry]
registry_server = "gcr.io"
registry_provider = "GCP"
molecule_container_name = "my-project/diffusion-molecule-container"
molecule_container_tag = "latest-amd64"

📁 Project Structure

role-name/
├── defaults/ tasks/ handlers/ files/ templates/ vars/
├── meta/
│   └── main.yml              # role metadata (auto-detected by diffusion)
├── scenarios/
│   └── default/
│       ├── converge.yml      # convergence playbook
│       ├── verify.yml        # verification tests
│       ├── molecule.yml      # molecule configuration
│       └── requirements.yml  # role & collection dependencies
├── diffusion.toml            # diffusion configuration
├── diffusion.lock            # locked dependency versions
└── .gitignore

🔄 CI/CD Integration

Use the --ci flag in CI/CD pipelines. It clones the repo inside the container (auto-detects git remote URL and commit SHA), removes -ti flags from docker exec, skips permission fixes, and disables spinner animations.

- name: Run Molecule tests
  run: diffusion molecule --ci --converge

- name: Verify
  run: diffusion molecule --ci --verify

- name: Check deps are locked
  run: diffusion deps check
molecule-test:
  script:
    - diffusion molecule --ci --converge
    - diffusion molecule --ci --verify
    - diffusion deps check

📦 Dependency Management

Diffusion provides a comprehensive dependency management system that validates Python versions, resolves tool versions from PyPI, manages Ansible Galaxy collections, generates lock files, and dynamically passes pyproject.toml to the container.

Python version management

Only three Python versions are supported: 3.13, 3.12, 3.11. All versions use major.minor format only.

InputNormalized To
pinned = "3.13.11"pinned = "3.13"
min = "3.11.9"min = "3.11"
pinned = "3.10"ERROR — not an allowed version

Only the pinned version is passed to the container as PYTHON_PINNED_VERSION.

Tool version constraints

Specify constraints in diffusion.toml. Supported operators: >=, <=, ==, >, <.

[dependencies]
ansible = ">=13.0.0"
molecule = ">=24.0.0"
ansible_lint = ">=24.0.0"
yamllint = ">=1.35.0"

[dependencies.python]
min = "3.11"
max = "3.13"
pinned = "3.13"

[[dependencies.collections]]
name = "community.general"
version = ">=7.4.0"

Lock file structure

version: "1.0"
generated: "2024-12-26T10:00:00Z"
hash: "abc123..."

python:
  min: "3.11"
  max: "3.13"
  pinned: "3.13"

tools:
  - name: ansible
    version: ">=13.0.0"
    resolved_version: "13.1.0"
    type: tool
    source: pypi

collections:
  - name: community.general
    version: ">=7.4.0"
    resolved_version: "7.5.0"
    type: collection
    source: galaxy
    python_deps:
      docker: "7.1.0"
      requests: "2.32.3"

roles:
  - name: geerlingguy.docker
    version: ">=7.0.0"
    resolved_version: "7.4.1"
    type: role
    source: galaxy

The hash is computed from all dependency names, versions, and Python config. diffusion deps check recomputes and compares it.

Version compatibility

Diffusion automatically validates tool compatibility with Python versions. If you specify incompatible versions, it warns and adjusts.

PythonAnsibleMoleculeansible-lint
3.138–135–256–24
3.128–135–256–24
3.118–135–256–24

Dependency priority

When the same dependency is specified in multiple places:

  1. diffusion.toml (highest priority)
  2. requirements.yml
  3. meta/main.yml (lowest priority)

Workflow

diffusion deps init       # add [dependencies] to diffusion.toml
diffusion deps lock       # resolve from PyPI/Galaxy, write diffusion.lock
diffusion deps resolve    # pretty-print resolved versions
diffusion deps check      # verify lock is up-to-date (CI gate)
diffusion deps sync       # write locked versions back to requirements.yml

⚡ Cache Feature

The cache feature persists the .ansible directory from the Molecule container to your local filesystem, significantly speeding up subsequent runs by caching downloaded collections, roles, Docker images, and Python packages.

How it works

  1. A unique cache ID is generated for your role
  2. Cache directory created at ~/.diffusion/cache/role_<cache_id>/
  3. Subdirectories roles/ and collections/ are mounted to /root/.ansible/roles and /root/.ansible/collections in the container
  4. On subsequent runs, artifacts are reused instead of re-downloaded

Performance impact

Without CacheWith Cache
First run3–5 minutesSame (builds cache)
Subsequent runs3–5 minutes30s – 1 min
Network~50 MB downloadMinimal

Configuration

[cache]
enabled = true
cache_id = "a1b2c3d4e5f6g7h8"
# cache_path = "custom/path"  # optional override

Docker integration

docker run ... \
  -v ~/.diffusion/cache/role_<id>/roles:/root/.ansible/roles \
  -v ~/.diffusion/cache/role_<id>/collections:/root/.ansible/collections \
  ...

Best practices

  • Enable cache during active development for faster iterations
  • Clean cache periodically with diffusion cache clean
  • Disable cache in CI/CD for clean builds
  • Share cache_id in team docs for consistent caching

Directory structure

~/.diffusion/
└── cache/
    ├── role_a1b2c3d4e5f6g7h8/
    │   ├── collections/
    │   └── roles/
    └── role_9f8e7d6c5b4a3210/
        ├── collections/
        └── roles/

🔑 Artifact Management

Diffusion supports managing multiple private artifact repository credentials with secure encrypted storage. Credentials can be stored locally (encrypted) or in HashiCorp Vault.

Security

Encryption

  • Algorithm: AES-256-GCM
  • Key: SHA-256 of hostname:username:diffusion-artifact-secrets
  • Nonce: randomly generated per operation
  • Storage: ~/.diffusion/secrets/<role>/<source>
  • Permissions: 0600 (owner only)

Properties

  • Machine-specific — cannot decrypt on another machine
  • User-specific — each user has own key
  • Authenticated encryption (GCM)
  • Key derived on-demand, never stored

Adding credentials

$ diffusion artifact add my-private-repo
Enter URL for my-private-repo: https://artifacts.example.com
Store credentials in Vault? (y/N): n
Enter Username: myuser
Enter Token/Password: ********
Credentials saved (encrypted in ~/.diffusion/secrets/<role>/my-private-repo)
[[artifact_sources]]
name = "my-private-repo"
url = "https://artifacts.example.com"
use_vault = false
$ diffusion artifact add vault-repo
Enter URL: https://vault-artifacts.example.com
Store credentials in Vault? (y/N): y
Enter Vault path: secret/data/prod
Enter Vault secret name: vault-repo
Enter Username Field (default: username): git_username
Enter Token Field (default: token): git_token
[[artifact_sources]]
name = "vault-repo"
url = "https://vault-artifacts.example.com"
use_vault = true
vault_path = "secret/data/prod"
vault_secret_name = "vault-repo"
vault_username_field = "git_username"
vault_token_field = "git_token"

[vault]
enabled = true
[[artifact_sources]]
name = "dev-repo"
url = "https://dev.example.com"
use_vault = false  # stored locally

[[artifact_sources]]
name = "prod-repo"
url = "https://prod.example.com"
use_vault = true   # stored in Vault
vault_path = "secret/data/prod"
vault_secret_name = "artifacts"
vault_username_field = "git_username"
vault_token_field = "git_token"

[vault]
enabled = true

Environment variables

When running molecule, Diffusion sets indexed environment variables for each source:

GIT_USER_1=user1      GIT_PASSWORD_1=token1    GIT_URL_1=https://repo1.example.com
GIT_USER_2=user2      GIT_PASSWORD_2=token2    GIT_URL_2=https://repo2.example.com
# ... up to GIT_*_10 (configurable via MaxArtifactSources)

Using in Ansible

- name: Clone from private repository
  ansible.builtin.git:
    repo: "{{ lookup('env', 'GIT_URL_1') }}"
    dest: /opt/myapp
    version: main
  environment:
    GIT_USERNAME: "{{ lookup('env', 'GIT_USER_1') }}"
    GIT_PASSWORD: "{{ lookup('env', 'GIT_PASSWORD_1') }}"

Troubleshooting

  • Cannot decrypt after moving machines — credentials are machine-specific, re-add them
  • Permission denied — chmod 700 ~/.diffusion/secrets
  • Vault connection failed — verify VAULT_ADDR, VAULT_TOKEN, and path/secret config

🔨 Building from Source

Requires Go 1.21+ and Make. Go supports cross-compilation out of the box — build for any platform from any platform.

Main targets

CommandDescription
make buildBuild for current platform
make distBuild for all platforms
make linuxAll Linux architectures
make darwinAll macOS architectures
make windowsAll Windows architectures
make testRun unit tests
make cleanRemove build artifacts

Platform-specific targets

# Linux
make linux-amd64    # x86_64
make linux-arm64    # ARM64, Raspberry Pi 4+
make linux-arm      # Raspberry Pi 3, older ARM

# macOS
make darwin-amd64   # Intel Macs
make darwin-arm64   # Apple Silicon (M1/M2/M3)

# Windows
make windows-amd64  # Standard PCs
make windows-arm64  # Surface Pro X, etc.
make windows-arm    # Older ARM devices

Output

bin/
├── diffusion-linux-amd64
├── diffusion-linux-arm64
├── diffusion-linux-arm
├── diffusion-darwin-amd64
├── diffusion-darwin-arm64
├── diffusion-windows-amd64.exe
├── diffusion-windows-arm64.exe
└── diffusion-windows-arm.exe

Build flags

The Makefile uses -s -w (strip symbol table and DWARF info) and injects version via -X. You can specify a version: make VERSION=1.0.0 dist.

Manual build (without Make)

GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o bin/diffusion-linux-amd64 .
GOOS=darwin GOARCH=arm64 go build -ldflags "-s -w" -o bin/diffusion-darwin-arm64 .
GOOS=windows GOARCH=amd64 go build -ldflags "-s -w" -o bin/diffusion-windows-amd64.exe .

CI/CD build examples

name: Build
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-go@v4
        with:
          go-version: '1.21'
      - run: make dist
      - uses: actions/upload-artifact@v3
        with:
          name: binaries
          path: bin/
build:
  image: golang:1.21
  script:
    - make dist
  artifacts:
    paths:
      - bin/

✅ Binary Verification

All releases are signed with Cosign and include SLSA Level 3 provenance for supply chain security.

Security layers

  1. SHA256 checksums — verify file integrity
  2. Cosign signatures — verify authenticity (keyless via Sigstore)
  3. SLSA Level 3 provenance — verify build process and supply chain

Quick verification

# 1. Verify checksum
sha256sum --check SHA256SUMS --ignore-missing

# 2. Verify Cosign signature
cosign verify-blob \
  --certificate diffusion-linux-amd64.tar.gz.pem \
  --signature diffusion-linux-amd64.tar.gz.sig \
  --certificate-identity-regexp="https://github.com/Polar-Team/diffusion" \
  --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \
  diffusion-linux-amd64.tar.gz

# 3. Verify SLSA provenance
slsa-verifier verify-artifact diffusion-linux-amd64.tar.gz \
  --provenance-path multiple.intoto.jsonl \
  --source-uri github.com/Polar-Team/diffusion \
  --source-tag v1.0.0

Platform-specific

sudo apt install cosign gh
# or: sudo dnf install cosign gh
# Install slsa-verifier from GitHub releases

cosign verify-blob \
  --certificate diffusion-linux-amd64.tar.gz.pem \
  --signature diffusion-linux-amd64.tar.gz.sig \
  --certificate-identity-regexp="https://github.com/Polar-Team/diffusion" \
  --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \
  diffusion-linux-amd64.tar.gz
brew install cosign gh slsa-verifier

cosign verify-blob \
  --certificate diffusion-darwin-arm64.tar.gz.pem \
  --signature diffusion-darwin-arm64.tar.gz.sig \
  --certificate-identity-regexp="https://github.com/Polar-Team/diffusion" \
  --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \
  diffusion-darwin-arm64.tar.gz
choco install cosign gh

cosign verify-blob `
  --certificate diffusion-windows-amd64.zip.pem `
  --signature diffusion-windows-amd64.zip.sig `
  --certificate-identity-regexp="https://github.com/Polar-Team/diffusion" `
  --certificate-oidc-issuer="https://token.actions.githubusercontent.com" `
  diffusion-windows-amd64.zip

SLSA Level 3

  • Build integrity — isolated and tamper-proof
  • Provenance — generated automatically
  • Non-falsifiable — cryptographically signed
  • Dependency tracking — recorded in provenance

🔒 Unix Permissions

Docker containers run as root (required for Docker-in-Docker), which causes permission issues on Linux/macOS. Diffusion uses a hybrid approach.

How it works

Main Molecule Container

Runs as ROOT (required for DinD). After operations, ownership is fixed inside the container: chown -R UID:GID /opt/molecule.

AnsibleGalaxyInit Container

Runs with user mapping (--user UID:GID). Creates role structure with correct ownership from the start.

Windows

Containers run as root (default). No permission fixes needed due to Windows filesystem model.

Permission fixing

// Runs inside container where we have root privileges
chownCmd := fmt.Sprintf("chown -R %d:%d /opt/molecule", uid, gid)
dockerExecInteractiveHide(RoleFlag, "/bin/sh", "-c", chownCmd)

Called after: ansible-galaxy role init, molecule converge, and all molecule operations.

Troubleshooting

  • Molecule folder owned by root — permission fix didn't run; check logs
  • "operation not permitted" — fix now runs inside container (should not occur)
  • Cannot access Docker inside container — ensure main container runs as root (no --user flag)

📌 Role Version Constraints

Roles support version constraints across three files: diffusion.toml (constraints), diffusion.lock (constraints + resolved), requirements.yml (resolved versions).

Constraint logic

Input ConstraintResolved VersionStored in diffusion.toml
(empty)1.2.3>=1.2.3
latest1.2.3>=1.2.3
main1.2.3>=1.2.3
>=1.0.01.2.3>=1.0.0
==1.0.01.0.0==1.0.0

Adding a role with constraints

diffusion role add-role geerlingguy.docker \
  --src https://github.com/geerlingguy/ansible-role-docker.git \
  --version ">=6.0.0" \
  --scenario default

This resolves the latest version from Galaxy, adds to requirements.yml with the resolved version, and adds to diffusion.toml with the constraint.

Git version resolution

For git repositories, Diffusion fetches all tags via git ls-remote, compares each against the constraint, and returns the latest matching tag. Supports >=, <=, ==, >, <.

File structure

# diffusion.toml — stores constraints
[[dependencies.roles]]
name = "default.geerlingguy.docker"
src = "https://github.com/geerlingguy/ansible-role-docker.git"
scm = "git"
version = ">=6.0.0"

# diffusion.lock — stores both
roles:
  - name: geerlingguy.docker
    version: ">=6.0.0"
    resolved_version: "6.1.0"

# requirements.yml — stores resolved
roles:
  - name: geerlingguy.docker
    src: https://github.com/geerlingguy/ansible-role-docker.git
    scm: git
    version: "6.1.0"

🔄 Migration Guide

Migrating from single-URL artifact configuration to the new multi-source structure.

What changed

url = "https://artifacts.example.com"

[vault]
enabled = true
secret_kv2_path = "secret/data/git"
secret_kv2_name = "credentials"
username_field = "git_username"  # REMOVED — now per-source
token_field = "git_token"        # REMOVED — now per-source
[[artifact_sources]]
name = "primary"
url = "https://artifacts.example.com"
use_vault = true
vault_path = "secret/data/git"
vault_secret_name = "credentials"
vault_username_field = "git_username"  # now per-source
vault_token_field = "git_token"        # now per-source

[vault]
enabled = true  # simplified — no field names here

Migration options

Automatic

Delete diffusion.toml and run diffusion molecule --role test --org test. Follow the interactive prompts.

Manual

Edit diffusion.toml directly: replace url with [[artifact_sources]] blocks, move Vault field names to per-source config.

Backward compatibility

The old configuration still works but shows a deprecation notice. Environment variables (GIT_USER_1, etc.) remain unchanged — Ansible playbooks don't need updates.

Secrets storage path change

  • Old: ~/.diffusion/<source>_artifact_secrets
  • New: ~/.diffusion/secrets/<role>/<source>

Breaking changes

  • Vault field names (username_field, token_field) moved from [vault] to per-source [[artifact_sources]]
  • diffusion role without --init now displays config instead of prompting to initialize