Copier Installation

This guide covers all installation options for Copier.

Best for developers who already have Python installed.

# Using pipx (recommended for CLI tools)
pipx install copier

# Or using pip
pip install copier

# Verify installation
copier --version  # Should be 9.17.0 or higher

Option 2: Docker

Best for users who don’t want to install Python. This script runs Copier in a container and handles path resolution automatically.

Prerequisites

  • Docker installed and running

  • Git credentials in ~/.git-credentials (for private repositories)

Setup

Save this script as copier.sh in a convenient location (e.g., ~/bin/copier.sh):

copier.sh (click to expand)
#!/bin/bash
# Wrapper script to run Copier in a container
# Usage: ./copier.sh copy <template-url-or-path> <destination> [options]
#        ./copier.sh update [destination] [options]

set -e

IMAGE_NAME="ngss-copier"
IMAGE_TAG="latest"

# Build image if it doesn't exist
if ! docker image inspect "${IMAGE_NAME}:${IMAGE_TAG}" &>/dev/null; then
    echo "Building Copier Docker image..."
    docker build -t "${IMAGE_NAME}:${IMAGE_TAG}" - << 'DOCKERFILE'
FROM python:3.12-slim
RUN apt-get update && apt-get install -y --no-install-recommends git && rm -rf /var/lib/apt/lists/*
RUN pip install --no-cache-dir copier>=9.7.0 copier-templates-extensions>=0.3.0 pyyaml>=6.0
RUN git config --global --add safe.directory '*' && \
    git config --global credential.helper 'store --file=/root/.git-credentials' && \
    git config --global init.defaultBranch main
ENV GIT_TERMINAL_PROMPT=0
WORKDIR /project
ENTRYPOINT ["copier"]
CMD ["--help"]
DOCKERFILE
fi

# Parse arguments to find and resolve paths
ARGS=()
DEST_PATH=""
SRC_PATH=""
CMD=""
POSITIONAL_COUNT=0

for arg in "$@"; do
    if [[ -z "$CMD" && "$arg" =~ ^(copy|update|recopy)$ ]]; then
        CMD="$arg"
        ARGS+=("$arg")
    elif [[ "$CMD" == "copy" || "$CMD" == "recopy" ]] && [[ ! "$arg" =~ ^- ]]; then
        POSITIONAL_COUNT=$((POSITIONAL_COUNT + 1))
        if [[ $POSITIONAL_COUNT -eq 1 ]]; then
            # First positional after copy is source (template)
            if [[ "$arg" =~ ^https?:// ]] || [[ "$arg" =~ ^git@ ]] || [[ "$arg" =~ ^gh: ]]; then
                # Remote URL, pass through
                ARGS+=("$arg")
            elif [[ -d "$arg" ]]; then
                # Local directory - resolve and mount
                SRC_PATH="$(cd "$arg" && pwd)"
                ARGS+=("/src")
            else
                # Might be a git ref, pass through
                ARGS+=("$arg")
            fi
        elif [[ $POSITIONAL_COUNT -eq 2 ]]; then
            # Second positional is destination
            if [[ -d "$arg" ]]; then
                DEST_PATH="$(cd "$arg" && pwd)"
            else
                mkdir -p "$arg"
                DEST_PATH="$(cd "$arg" && pwd)"
            fi
            ARGS+=("/dest")
        else
            ARGS+=("$arg")
        fi
    elif [[ "$CMD" == "update" ]] && [[ -z "$DEST_PATH" ]] && [[ ! "$arg" =~ ^- ]] && [[ -d "$arg" ]]; then
        # For update, first path-like arg is destination
        DEST_PATH="$(cd "$arg" && pwd)"
        ARGS+=("/dest")
    else
        ARGS+=("$arg")
    fi
done

# Default destination to current directory if not specified
if [[ -z "$DEST_PATH" ]]; then
    DEST_PATH="$(pwd)"
    if [[ "$CMD" == "update" ]]; then
        ARGS+=("/dest")
    fi
fi

# Build docker run args
DOCKER_ARGS=(
    --rm -it
    -v "${DEST_PATH}:/dest"
    -v "${HOME}/.gitconfig:/root/.gitconfig:ro"
    -v "${HOME}/.ssh:/root/.ssh:ro"
    -e "GIT_AUTHOR_NAME=${GIT_AUTHOR_NAME:-$(git config user.name 2>/dev/null || echo '')}"
    -e "GIT_AUTHOR_EMAIL=${GIT_AUTHOR_EMAIL:-$(git config user.email 2>/dev/null || echo '')}"
    -e "GIT_COMMITTER_NAME=${GIT_COMMITTER_NAME:-$(git config user.name 2>/dev/null || echo '')}"
    -e "GIT_COMMITTER_EMAIL=${GIT_COMMITTER_EMAIL:-$(git config user.email 2>/dev/null || echo '')}"
    # Override macOS credential helper with store helper
    -e "GIT_CONFIG_COUNT=1"
    -e "GIT_CONFIG_KEY_0=credential.helper"
    -e "GIT_CONFIG_VALUE_0=store --file=/root/.git-credentials"
)

# Mount source template if it's a local path
if [[ -n "$SRC_PATH" ]]; then
    DOCKER_ARGS+=(-v "${SRC_PATH}:/src:ro")
fi

# Mount git-credentials if it exists
if [[ -f "${HOME}/.git-credentials" ]]; then
    DOCKER_ARGS+=(-v "${HOME}/.git-credentials:/root/.git-credentials:ro")
fi

# Mount copier settings.yml if it exists (check platform-specific locations)
COPIER_SETTINGS=""
if [[ -f "${HOME}/Library/Application Support/copier/settings.yml" ]]; then
    # macOS
    COPIER_SETTINGS="${HOME}/Library/Application Support/copier/settings.yml"
elif [[ -f "${HOME}/.config/copier/settings.yml" ]]; then
    # Linux
    COPIER_SETTINGS="${HOME}/.config/copier/settings.yml"
fi
if [[ -n "$COPIER_SETTINGS" ]]; then
    # Mount to Linux config location inside container
    DOCKER_ARGS+=(-v "${COPIER_SETTINGS}:/root/.config/copier/settings.yml:ro")
fi

# Run copier
docker run "${DOCKER_ARGS[@]}" "${IMAGE_NAME}:${IMAGE_TAG}" "${ARGS[@]}"

Make it executable and add to PATH:

chmod +x ~/bin/copier.sh
export PATH="$HOME/bin:$PATH"  # Add to ~/.bashrc or ~/.zshrc

Docker Usage Examples

# Copy DevOps template
copier.sh copy https://coderepo.mobilehealth.va.gov/scm/ckm/ngss-devops-template.git .

# Update existing project
copier.sh update -a .copier-answers/devops.yaml

# Use local template (for development)
copier.sh copy ../ngss-devops-template . --trust

How It Works

The script:

  1. Builds the Docker image on first run (auto-rebuilds if missing)

  2. Parses arguments to detect source and destination paths

  3. Resolves relative paths and mounts them into the container

  4. Mounts git credentials (~/.git-credentials) and SSH keys (~/.ssh/)

  5. Mounts copier settings.yml (from macOS or Linux location) for trust configuration

  6. Overrides macOS credential helper to use the mounted credentials

Rebuild the Image

If you need to update the Copier version:

docker rmi ngss-copier:latest
copier.sh --help  # Triggers rebuild

Option 3: Maven Plugin

Projects using ngss-maven-tiles 6.x+ can use Maven goals as wrappers:

mvn filegen:copier-init                              # Initialize all standard templates
mvn filegen:copier-init -Dcopier.template=devops     # Initialize DevOps template
mvn filegen:copier-update                            # Update all templates
mvn filegen:copier-update -Dcopier.template=devops   # Update DevOps template
Maven goals still require Copier to be installed via pip or Docker.

Trusting Templates

NGSS templates use Jinja extensions and tasks which require trust. You can either:

  1. Add --trust to every command, or

  2. Configure trusted repositories in settings.yml (recommended)

Configuring Trusted Repositories

Create the settings file at the platform-specific location:

Platform Path

macOS

~/Library/Application Support/copier/settings.yml

Linux

~/.config/copier/settings.yml

Windows

%APPDATA%\copier\settings.yml

Add the NGSS templates to the trust list:

trust:
  - https://coderepo.mobilehealth.va.gov/scm/ckm/ngss-devops-template.git
  - https://coderepo.mobilehealth.va.gov/scm/ckm/ngss-backstage-template.git
Use a trailing slash to trust all templates under a path: https://coderepo.mobilehealth.va.gov/scm/ckm/
The file must be named settings.yml (not settings.yaml).

Git Credentials

HTTPS Authentication

Copier needs credentials for private repositories. Create ~/.git-credentials:

# Format: https://username:token@host
echo "https://username:YOUR_TOKEN@coderepo.mobilehealth.va.gov" >> ~/.git-credentials
chmod 600 ~/.git-credentials

Troubleshooting

"Template requires trust" error

Either add --trust to the command or configure trusted repositories in settings.yml.

"terminal prompts disabled" Error (Docker)

Git credentials are not being found:

  1. Verify ~/.git-credentials exists

  2. Check the format: https://username:token@host

  3. Ensure the file is readable

"credential-osxkeychain is not a git command" (Docker)

This happens when your .gitconfig references the macOS Keychain. The Docker script overrides this, but ensure you have ~/.git-credentials as a fallback.

Permission Denied on ~/.ssh (Docker)

chmod 700 ~/.ssh
chmod 600 ~/.ssh/*