ggraft
GLSL Graft is a cli tool allowing declarative injection patches for vanilla Minecraft shaders.
Overriding a core shader means copying Mojang's file and changing one line. Do that across twenty shaders and every Minecraft update becomes twenty manual re-copies, with no record of what you actually changed. ggraft keeps the edit under version control instead of the result: you declare which line to change, and it re-derives the overrides from any version on demand.
Terminology
| Concept | Name |
|---|---|
| The vanilla GLSL going in | base |
| A file declaring edits | patch |
| Which shader it's for | target |
| Where in the source | injection point (or anchor) |
| One edit inside a patch | injection |
| What it does | operation: insert, replace, wrap, declare |
| Anchor didn't match | unresolved injection |
| Anchor matched N times | ambiguous injection |
| GLSL coming out | patched shader |
Install
From PyPI
pip install ggraft
From source
Needs Python 3.11+ and Click. Everything else is standard library.
pip install -e .
Quick start
ggraft init 26.3 # write ggraft.toml
ggraft pull # fetch bases from misode/mcmeta
ggraft build # apply patches -> patched shaders
Project configuration
ggraft.toml, found by walking up from the working directory. Paths resolve relative to it.
[base]
version = "26.3" # Minecraft version to pull
dir = ".ggraft/base" # where bases land (gitignore this)
source = "assets/minecraft/shaders" # subtree to pull, stripped from local paths
repo = "misode/mcmeta" # optional
[patches]
dir = "patches"
[output]
dir = "assets/minecraft/shaders" # required
header = true # stamp a provenance comment
Targets are relative to base.source: core/terrain.vsh reads from .ggraft/base/core/terrain.vsh and writes to <output.dir>/core/terrain.vsh.
Patches
name = "iso-world"
targets = ["core/terrain.vsh", "core/rendertype_*.vsh"]
exclude = ["core/rendertype_lines.vsh"]
[[injection]]
op = "declare"
include = "foo:iso.glsl"
[[injection]]
op = "replace"
at = "gl_Position = ProjMat * ModelViewMat * vec4({expr}, 1.0);"
with = "gl_Position = iso_position(ProjMat, ModelViewMat, vec4({expr}, 1.0));"
Injections apply in order, to every matching target. Patches stack in filename order.
Anchors
at is literal text, not a regex. {name} captures what sits in that position, {match} is the whole match; both are available in the replacement.
| Key | Meaning |
|---|---|
at |
the anchor pattern |
regex = true |
treat at as a regex, with named groups |
multiline = true |
let one {name} (or a regex .) span lines (false by default) |
occurrence = N |
take the Nth match (1-based) |
every = true |
apply to all matches |
No match is an unresolved injection; more than one without occurrence or every is an ambiguous injection. Both fail the build, naming the patch, injection index, target and line numbers.
Operations
| Operation | Keys | Effect |
|---|---|---|
replace |
with |
swap the anchored text |
insert |
text, where (before/after) |
add text beside it |
wrap |
prefix, suffix |
surround it, keeping it |
declare |
include or text |
add a top-level declaration; no anchor |
declare inserts after the last #include at conditional-nesting depth zero, falling back to #extension then #version, and is idempotent. Appending after the last #include in the file would strand it inside an #ifdef branch that some shader variants skip.
Commands
| Command | Purpose |
|---|---|
ggraft pull [version] |
fetch bases; --force refetches, -j sets parallelism. Pulling another version replaces the base |
ggraft build |
apply patches; --check dry run, -t filters by glob |
ggraft targets |
show which patches hit which targets |
ggraft init [version] |
write a starter config; --force overwrites |
-h/--help works on the group and every command, -V/--version prints the version, and -c/--config points at a specific ggraft.toml instead of searching upwards. pull --token also reads GITHUB_TOKEN.
Build guarantees
--checkdry run without output writes.- A target pattern matching no base file fails the build. What a patch orphaned by a Minecraft update looks like.
- A patch applying to nothing fails the build, so a mistake in
targetsorexcludecannot disable a patch silently. - Stale output is pruned. Shaders ggraft wrote are recorded in
.ggraft/manifest.json, and a target dropped from a patch has its override deleted on the next build. Only manifest entries are ever deleted;-tdisables pruning, since a partial build cannot tell what is stale.
Bases
Uses misode/mcmeta.
Tree walk might be rate limited. Set GITHUB_TOKEN or pass --token to bypass the limit.
A pull downloads into a sibling .partial directory and swaps it in, so an interrupted pull leaves the existing base untouched and a completed one leaves nothing of it behind: base.dir is replaced wholesale, never merged into.
What the directory holds — version, repo and source — is recorded in .ggraft/base.json. Pulling a different version, repo or source therefore refetches instead of reporting the base as already present, and files from the old version cannot linger as targets for the next build. A base ggraft did not record is treated as stale and refetched too. --force refetches regardless.
Tests
python -m unittest discover -s tests # everything
python -m unittest tests.test_cli # one module
One module per layer: test_structure, test_glsl, test_anchor, test_operations, test_engine, test_sources, test_cli. Shared shaders, project files and a network-free mcmeta client live in tests/fixtures.py.
Planned Features
- functions injections
- python module API
Metadata
Release files for ggraft 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| ggraft-1.0.0.tar.gz | 30.5 kB | With provenance | Details for ggraft-1.0.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| ggraft-1.0.0-py3-none-any.whl | Python 3 | none | any | Details for ggraft-1.0.0-py3-none-any.whl |
Total release size: 52.3 kB
Release files / ggraft-1.0.0.tar.gz
| Download URL | ggraft-1.0.0.tar.gz |
|---|---|
| Size | 30.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
542b94989d79f71d43cfd507eeca65b7c8547eb50d5097ad990ec2a55da4b455
|
|
BLAKE2b-256 checksum How to use checksums |
19a8e0ba4708d3287528b2668f248fff30bb9fbd7f8ae83ddabd4ecd065a4098
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.
Transparency logRelease files / ggraft-1.0.0-py3-none-any.whl
| Download URL | ggraft-1.0.0-py3-none-any.whl |
|---|---|
| Size | 21.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
019719c5e58477a0e77f81de13cd666db609cc9928203e4a9a6ef1d49157ae53
|
|
BLAKE2b-256 checksum How to use checksums |
33d709b83167545afcefca4d4d56355268e6ba57affcc27db9a61f7bc1a588d6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.
Transparency logSecurity for ggraft 1.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for ggraft-1.0.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 18, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.