Skip to content

chore(templater): migrate the template engine from slim-sprig to sprout - #2968

Draft
vmaerten wants to merge 4 commits into
mainfrom
chore/templating-sprout
Draft

chore(templater): migrate the template engine from slim-sprig to sprout#2968
vmaerten wants to merge 4 commits into
mainfrom
chore/templating-sprout

Conversation

@vmaerten

Copy link
Copy Markdown
Member

Summary

Task's template functions came from slim-sprig, a fork of the unmaintained Masterminds/sprig that we keep alive ourselves. This migrates the engine to sprout, sprig's maintained successor, whose registry system gives the same trimmed function set without a fork to maintain — crypto and network are left out, so the exposed surface stays at slim-sprig parity. Task's own functions move into a sprout registry, every slim-sprig name still resolves through an alias table, and wrappers accept both the old and the new argument order for the ten functions sprout reordered, warning about the old one under --verbose.

Two things needed more than a name mapping. dig lost its default-value argument entirely in sprout (dig(keys..., dict) where sprig had dig(keys..., default, dict)), and the two forms cannot be told apart by type — Task keeps sprig's meaning, since that is what every existing Taskfile was written against. And merge, fromYaml, toYaml, mustFromYaml and mustToYaml are re-applied after the handler is built, because sprout ships functions and aliases of the same name with different semantics (deep merge, errors raised instead of swallowed).

Notices are routed to the Executor's logger at verbose level and deduplicated. sprout logs them to stdout by default, which would corrupt the group and --json output.

Reviewers should look at the behaviour changes below: they are corrections of long-standing sprig bugs and are not opt-in. Notably, functions that used to swallow an error or panic now fail the task instead of rendering an empty string — {{ atoi "abc" }} and {{ fromJson "not json" }} are the common cases.

Closes #1638. Supersedes #2006 by @42atomys, whose approach this follows.

Test plan

The first commit is a safety net, generated against the slim-sprig implementation before anything changed: two golden files in internal/templater/testdata/, one listing every template function name, the other the rendered output of ~90 representative expressions. The diff after the switch is the exhaustive, auditable inventory of what changed for users.

  • No function name disappeared (194 → 272 names).
  • No regression in slim-sprig argument order: the eleven expressions of the first golden group render identically.
  • The behaviour diff is seven intentional entries: substr with a negative index ({{ substr 0 -3 "foobar" }} now foo, was foobar), title with Unicode casing ({{ title "hello wORLD" }} now Hello World, was Hello WORLD), atoi and fromJson reporting errors, {{ duration 90 }} now 1m30s instead of 0s, dig splitting keys on dots, and a reworded mustFromJson error.

Added tests: TestSprigSignatureShims covers the ten reordered functions in both orders, TestDigKeepsSprigDefault covers six dig cases, and TestTaskFuncsShadowSprout pins the functions that must win over their sprout namesakes.

go test ./... and golangci-lint run pass on the whole repository. No existing integration test or golden file needed touching.

Manual passes: deprecation notices are silent by default and appear once each under -v; --output group and --list-all --json keep a clean stdout, including under -v.

The binary grows from 72.3 MB to 73.3 MB (+1.3%), the cost of the wider function surface plus mergo, copystructure and cast.

The behaviour golden pins the local timezone to UTC in a TestMain, without which it would fail on CI. The date/toDate local-vs-UTC difference is therefore documented in the migration guide rather than captured there.

Golden tests capturing the sorted list of template function names and the
rendered output of a representative expression per function. Generated
against the current slim-sprig implementation so that any change of
templating library produces an auditable diff.
slim-sprig is a fork of the unmaintained Masterminds/sprig that Task keeps
alive itself. sprout is its maintained successor, and its registry system
gives the same trimmed function set without a fork to maintain.

Task's own functions move into a sprout registry. Ten functions changed
argument order between the two libraries, and dig lost its default-value
argument; wrappers accept both forms so existing Taskfiles keep working.
merge and fromYaml/toYaml are re-applied after the handler is built,
because sprout's namesakes have different semantics.

Closes #1638.
sprout logs its deprecation notices to stdout by default, which would
corrupt the JSON and group output styles. Route them to the Executor's
logger at verbose level instead, deduplicated so that a single deprecated
call is not reported once per compilation pass.
Add a migration section listing the renamed functions, the ten argument
order changes and the behaviour differences, extend the template function
deprecation page with the new aliases, and point the sprig links at the
sprout documentation.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrate from sprig (slim-sprig) to sprout?

1 participant