Skip to main content

Template Files

Render template files through the template engine and include them in releases

The template_files stage renders source files through the template engine and automatically uploads the output as release assets.

Minimal config

template_files:
  - src: install.sh.tpl
    dst: install.sh

Template files config fields

FieldTypeDefaultDescription
idstringdefaultIdentifier for this template file entry
srcstringSource template file path (supports templates)
dststringDestination filename within the dist directory (supports templates)
modestring0655File permissions in octal notation (Unix only)

Behavior

  • Both src and dst paths are rendered through the template engine before use
  • The source file contents are also rendered through the template engine
  • Output files are written to dist/<dst>
  • Each output is registered as an uploadable artifact (included in releases, checksums, and signing)
  • Path traversal (..) and absolute paths in dst are rejected for security
  • The stage can be skipped with --skip templatefiles

Template rendering

Use any template variable in both the file paths and file contents:

template_files:
  - id: install-script
    src: "scripts/{{ ProjectName }}-install.sh.tpl"
    dst: "{{ ProjectName }}-{{ Version }}-install.sh"
    mode: "0755"

Given a source file scripts/myapp-install.sh.tpl:

#!/bin/sh
# Install {{ ProjectName }} {{ Version }}
curl -L https://github.com/myorg/{{ ProjectName }}/releases/download/{{ Tag }}/{{ ProjectName }}-{{ Os }}-{{ Arch }}.tar.gz | tar xz

Setting file permissions

Use the mode field to set executable permissions on generated scripts:

template_files:
  - src: run.sh.tpl
    dst: run.sh
    mode: "0755"

The mode must be a string in octal notation (e.g., "0755", "0644"). The default is 0655.

Multiple template files

template_files:
  - id: install-script
    src: install.sh.tpl
    dst: install.sh
    mode: "0755"
  - id: config-example
    src: config.yaml.tpl
    dst: config.example.yaml
  - id: completion
    src: completions/bash.tpl
    dst: "{{ ProjectName }}.bash"

Each entry gets its own artifact ID, so you can reference them individually in publisher configs.

Remote installer case tables

Four template variables carry engine-generated POSIX-sh snippets for a curl | sh installer script, derived from the release's configured targets and the archive stage's own asset naming — so the script never hardcodes an asset name that 404s, and the detection arms track the same vocabulary that keys the asset arms instead of a hand-written uname mapping that silently drifts:

VariableContents
InstallerAssetCasescase "${OS}-${ARCH}" arms mapping each released os-arch pair to its exact asset filename (sets ARCHIVE=)
InstallerDetectOsCasescase "$(uname -s)" arms echoing the OS tokens the asset arms are keyed by
InstallerDetectArchCasescase "$(uname -m)" arms echoing the arch tokens the asset arms are keyed by
InstallerSupportedPlatformsThe reachable os-arch keys, space-joined — for error messages that list what IS available
#!/bin/sh
detect_os() {
    case "$(uname -s)" in
{{ InstallerDetectOsCases }}
        *) echo "unsupported" ;;
    esac
}

detect_arch() {
    case "$(uname -m)" in
{{ InstallerDetectArchCases }}
        *) echo "unsupported" ;;
    esac
}

OS="$(detect_os)"; ARCH="$(detect_arch)"
case "${OS}-${ARCH}" in
{{ InstallerAssetCases }}
    *) echo "no prebuilt binary for ${OS}/${ARCH}" >&2; exit 1 ;;
esac
curl -sSfL "https://github.com/me/{{ ProjectName }}/releases/download/{{ Tag }}/${ARCHIVE}"

Rendered for a release targeting Linux/macOS/Windows on amd64+arm64, the detection arms come out as:

        Linux*) echo "linux" ;;
        Darwin*) echo "darwin" ;;
        MINGW*|MSYS*|CYGWIN*) echo "windows" ;;

and each asset arm resolves to the same filename the archive stage uploads (ARCHIVE="myapp_1.2.3_linux_amd64.tar.gz"), including format_overrides (e.g. zip on Windows). A darwin-universal build is fanned out to the darwin-amd64 / darwin-arm64 keys, with arch-specific assets taking precedence.

The mips family is deliberately absent from the generated uname -m arms: uname -m reports mips/mips64 for both endiannesses, so the script cannot safely choose between same-name big- and little-endian assets — mips hosts get the explicit unsupported-platform error rather than a wrong-endian binary. illumos hosts (uname -s = SunOS, mapped to solaris) are likewise undetectable. Releasing such a target still emits its asset arm, but anodizer prints a warning naming the stranded target so you know those hosts fall through to the error path.

Each snippet omits the *) fallback arm — your template owns the error path. InstallerSupportedPlatforms is made for exactly that arm: it lists the keys a host can actually reach, so the error can point users at the assets that do exist:

    *)
        echo "Error: no prebuilt ${PROJECT} binary for ${OS}/${ARCH}" >&2
        echo "Prebuilt binaries: {{ InstallerSupportedPlatforms }}" >&2
        exit 1
        ;;

renders as:

        echo "Prebuilt binaries: darwin-amd64 darwin-arm64 linux-amd64 linux-arm64 windows-amd64 windows-arm64" >&2

All four variables render empty when no crate builds a binary named after the project with a binstallable archive.