Vendoring Alternatives: a plain copy or a git submodule
Getting Started brings webform-relay in with
v, which is the recommended and supported path.
Nothing in taskfiles/deploy.yml actually requires it, though: every task
resolves the vendored source through {{.TASKFILE_DIR}} and your project root
through {{.ROOT_DIR}}, so any layout that puts the source in a directory and
your .env / .env.project / webform.yaml in your project root works.
This page covers the two alternatives and what each costs you. Everything else — the dotenv split, bootstrap, deploy, rollback, teardown — is identical; follow Getting Started from step 3 onward. What differs is only how the source arrives, how it updates, and how the version is recorded.
Whichever you pick: don’t fork. A fork diverges immediately, and upstream bug fixes and IAM policy corrections never reach it short of a manual diff-and-reapply.
| How it arrives | How it updates | Recorded version | Support | |
|---|---|---|---|---|
v (recommended) | v add <url> <tag> | v update <url> <new-tag> | vendors.toml, pinned to an exact commit | Supported: this is the path the tasks are designed and tested for |
| Plain copy | release tarball or cp -r into your tree | Re-copy and diff by hand | Whatever you write down yourself | Works, but drift is on you |
| Git submodule | git submodule add | git submodule update --remote + commit the pointer | The submodule’s committed SHA | Works, at the cost of submodule ergonomics for everyone who clones |
A plain copy
Copy the source into your project — from a release tarball, or from a shallow
clone you then strip the .git from:
mkdir -p third_party/webform-relay
curl -sSL https://gitlab.com/frob/webform-relay/-/archive/<tag>/webform-relay-<tag>.tar.gz \
| tar -xz --strip-components=1 -C third_party/webform-relay(git archive --remote won’t work here — GitLab does not expose
git-upload-archive over HTTPS.)
Then point your Taskfile at it:
version: '3'
dotenv: ['.env', '.env.project']
includes:
relay:
taskfile: ./third_party/webform-relay/taskfiles/deploy.yml
dir: ./third_party/webform-relayand copy the dotenv templates out of it:
cp third_party/webform-relay/.env.example .env
cp third_party/webform-relay/.env.project .env.projectUpdating is re-copying the new release over the old directory and reading the diff yourself. Two things to do so that’s survivable:
- Write the version down. Nothing in a copy records where it came from.
Commit the tag and commit SHA in a
READMEor a one-line file next to the copied tree, so a later update knows what it’s updating from. - Never edit the copied tree. A local edit is invisible until the next update either clobbers it or shows up as a confusing conflict. Customize through your own Taskfile instead — see If you need to customize a task.
This is the right choice if you’re air-gapped, mirroring releases into an internal artifact store, or otherwise can’t fetch from a git host at build time.
A git submodule
git submodule add https://gitlab.com/frob/webform-relay vendor/webform-relay
cd vendor/webform-relay && git checkout <tag> && cd -
git add vendor/webform-relay && git commit -m "chore: vendor webform-relay <tag>"version: '3'
dotenv: ['.env', '.env.project']
includes:
relay:
taskfile: ./vendor/webform-relay/taskfiles/deploy.yml
dir: ./vendor/webform-relaycp vendor/webform-relay/.env.example .env
cp vendor/webform-relay/.env.project .env.projectThe submodule’s committed SHA is the pinned version, and
git submodule update --remote plus a commit of the new pointer is the update.
The cost is submodule ergonomics, borne by everyone who touches the project:
- A plain
git cloneof your project leaves the submodule empty, and the deploy tasks then fail with a missing-Dockerfile/missing-template.yamlerror rather than anything that names the real cause. Clone withgit clone --recurse-submodules, or rungit submodule update --init --recursiveafter the fact. - CI must be told too. On GitLab, set
GIT_SUBMODULE_STRATEGY: recursivein the job or globally; on GitHub Actions,with: submodules: recursiveonactions/checkout. Without it, the deploy job fails the same opaque way. - The submodule sits at a detached HEAD by design. Committing inside it is
possible and almost always a mistake — the commit belongs to nobody and is
lost on the next
update --remote. v updatehas no equivalent guardrail here:git submodule update --remotefollows the submodule’s tracked branch, so it can move you onto an unreleased commit. Pin a tag explicitly (git -C vendor/webform-relay checkout <tag>) and commit that.
What stays the same in all three
- The two-directory split. The vendored/copied/submoduled tree is read-only
source; your project root holds
Taskfile.yml,.env,.env.project, andwebform.yaml. See Two directories, not one. dotenv: ['.env', '.env.project']must be declared in your project’s own top-level Taskfile, never in the included file — Task refuses to load an included Taskfile that declares its owndotenv:..envis listed first so it wins over the committed.env.project..envis gitignored;.env.projectis committed. Both are copied out of the vendored source once, then maintained by you.- Task names,
CONFIG=resolution, and every path in Getting Started behave identically — only thetaskfile:/dir:pair in yourincludes:block changes. - Never edit the vendored/copied/submoduled source.
- The limitations and known gaps apply unchanged.
If you need to customize a task
Don’t edit the vendored taskfiles/deploy.yml — it’ll be overwritten. Add a
task with a different name to your own project Taskfile.yml instead, or
open an issue/PR upstream if the customization is generally useful.
If you do need file-level vars: in your own Taskfile, avoid reusing a name
like GO_MOUNTS, SAM_MOUNTS, AWS_ENV_FLAGS, TOOLS_IMAGE, or
STACK_NAME — Task does not fully isolate an included Taskfile’s file-level
vars from the includer’s when names collide, and taskfiles/deploy.yml’s own
vars are all prefixed RELAY_ specifically to make that vanishingly
unlikely from its side. A same-named var in your own Taskfile can still
silently clobber one of Task’s built-in vars used internally
(ROOT_DIR/TASKFILE_DIR themselves are safe — they’re not plain vars:
entries — but don’t name your own var ROOT_DIR or TASKFILE_DIR either).