Skip to main content

Apply customizations to any project without templating placeholders

Project description

engraft

Apply customizations to any project without templating placeholders.

The Problem

Customizing a project (e.g., white-label products) forces a choice between bad options:

  • Templating tools (Cookiecutter, Copier, Yeoman) require {{ placeholders }} in source code — the repo is no longer a working app
  • Forking leads to diverging codebases that are painful to sync with upstream
  • Manual editing is error-prone, undocumented, and impossible to reproduce

engraft solves this by keeping the source repo clean and runnable while providing a declarative, reproducible customization layer on top.

How It Works

engraft uses a two-file model:

  • Template file — defines what can be customized and how (maintained by the repo author)
  • Values file — contains the consumer's customization values

The original project stays untouched. Run engraft apply and the customizations are applied in place.

Installation

pip install engraft

Quick Start

Given a project with a config.json:

{
  "name": "DefaultApp",
  "version": "1.0.0"
}

Create a template file engraft.template.yml:

variables:
  app_name:
    description: Application name
    default: DefaultApp

customizations:
  - action: json_replace
    file: config.json
    replace:
      - selector: $.name
        variable: app_name

Create a values file engraft.values.yml:

app_name: MyApp

Apply:

engraft apply --template engraft.template.yml --values engraft.values.yml

Result — config.json now contains:

{
  "name": "MyApp",
  "version": "1.0.0"
}

Action Reference

json_replace

Replace values in JSON files using JSONPath-like selectors.

- action: json_replace
  file: app.json
  replace:
    - selector: $.expo.name
      variable: app_name
    - selector: $.expo.extra.items[0].label
      variable: item_label

Selectors use dot notation with optional array indices: $.path.to.key or $.array[0].field.

html_replace

Replace values in HTML files using XPath selectors. Supports both element text and attribute values.

- action: html_replace
  file: index.html
  replace:
    - selector: //title
      variable: page_title
    - selector: //meta[@name='description']/@content
      variable: page_description

The selector must match exactly one element or attribute. Matching zero or more than one is an error.

regex_replace

Replace values in any text file using regex with a named capture group.

- action: regex_replace
  file: src/theme.ts
  replace:
    - selector: '(PRIMARY_COLOR\s*=\s*)"(?P<value>[^"]*)"'
      variable: primary_color

The selector must contain a named capture group called value. Both syntaxes are accepted:

  • Python style: (?P<value>...)
  • ECMAScript/JavaScript style: (?<value>...)

Using the ECMAScript form keeps templates portable between the Python and TypeScript implementations. Only the captured group is replaced; the surrounding match is preserved.

file_replace

Replace an entire file with a source file referenced by a variable.

- action: file_replace
  file: assets/logo.png
  variable: logo

The variable value is a path relative to the values file directory. Useful for binary files like images.

Releasing

Versioning is automatic via hatch-vcs — the package version is derived from git tags.

To publish a new release:

  1. Create a GitHub Release with a tag matching vX.Y.Z (e.g., v0.2.0)
  2. The release pipeline automatically runs lint, format check, and tests
  3. If all checks pass, the package is built and published to PyPI with Sigstore signing

Values file notes

Values are parsed as YAML 1.2. The bare words yes, no, on, off (and their capitalized variants) parse as strings, not booleans — only true and false are coerced to booleans. If you are migrating a values file from older tooling that assumed YAML 1.1 boolean semantics, quote the value explicitly (flag: "true") when a string was intended.

Non-string scalars (numbers, booleans) are coerced to strings before being substituted into target files. A key whose value is null is treated as "not provided" — the variable's default (or the noop sentinel for optional variables) is used instead.

Development

Development commands are run from inside the python/ directory:

cd python/

# Install with dev dependencies
pip install -e ".[dev]"

# Run unit tests
pytest

# Run linter
ruff check src/ tests/

The repo also has an end-to-end harness under e2e/ (run from the repo root) that runs the same fixture scenarios against both the Python and TypeScript CLIs and asserts identical output.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

engraft-0.2.2.tar.gz (13.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

engraft-0.2.2-py3-none-any.whl (11.8 kB view details)

Uploaded Python 3

File details

Details for the file engraft-0.2.2.tar.gz.

File metadata

  • Download URL: engraft-0.2.2.tar.gz
  • Upload date:
  • Size: 13.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for engraft-0.2.2.tar.gz
Algorithm Hash digest
SHA256 7f857cdf9222b66eac1ae334c16595e711a7074327e7c07eb9ff2b2eaa9df390
MD5 fc1b8be1f4f6a4ceef37213d8b0422b8
BLAKE2b-256 aace7238e1966c5c505ff9b40fb470b9c9dd028e185fa869bab7a8b43059f070

See more details on using hashes here.

Provenance

The following attestation bundles were made for engraft-0.2.2.tar.gz:

Publisher: release.yml on smartcompanion-app/engraft

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file engraft-0.2.2-py3-none-any.whl.

File metadata

  • Download URL: engraft-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 11.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for engraft-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 ff40e352243ee12c48bfa7b3ea30e0f81be17616afae2285f51d26f6c306e07c
MD5 063f8181726fa30dc1be0b1b66af689c
BLAKE2b-256 9e59e2b9087cfb315dc0daf6803a1c51c2c54597d6dc2eff069982bb45c9bf04

See more details on using hashes here.

Provenance

The following attestation bundles were made for engraft-0.2.2-py3-none-any.whl:

Publisher: release.yml on smartcompanion-app/engraft

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page