# Taskfile Integration Exposing module commands through the `task` runner. Add a module `Taskfile.yml` only when its commands should be discoverable through `task`. A module that is only consumed by other modules does not need one. --- ## 1. Module `Taskfile.yml` ```yaml # https://taskfile.dev version: "3" tasks: initialize-one: cmds: - | ./.scripts/module-example-v1/api/initialize-one.sh {{.CLI_ARGS}} silent: true ``` Rules: 1. Task names mirror the public `api/` command names, unless an alias is deliberate. A task named differently from the script it runs makes `task --list` output impossible to map back to files. 2. Forward `{{.CLI_ARGS}}` whenever the command accepts flags. 3. `silent: true` suppresses task's own command echo, so only the script's logger output appears. 4. Invoke the `api/` wrapper — never a `lib/` file directly. 5. The path is repository-root-relative, matching how the scripts source their dependencies. Arguments pass through after `--`: ```bash task module-example-v1:initialize-one -- --name demo --dry-run ``` Without the `--`, task treats the flags as its own and the script never sees them. ### One env-driven task, not a `:default` pair Where the repository uses the layered environment model (`references/environment-model.md`), the env layers already supply every value a task needs. A module therefore needs **one** task, not a `` + `:default` pair that hardcodes what the environment already provides: ```yaml tasks: initialize-one: desc: Initialize using the .env layers (override with --flag=…) cmds: - ./.scripts/module-example-v1/api/initialize-one.sh {{.CLI_ARGS}} silent: true ``` A `:default` wrapper duplicates configuration in a second place, and the two drift. Environment-specific behaviour belongs in `.env-`; one-off overrides belong in flags after `--`. ### Composing commands When an operator-facing command already exists, prefer calling it through `task` rather than re-implementing or sourcing its internals, and pass parameters explicitly: ```bash task keycloak:realm-registration-enable-one -- --realm-id="${KEYCLOAK_ROOT_REALM_ID}" ``` Composing at the task surface keeps the public contract the only contract. Reaching into another module's `lib/` couples you to its internals and bypasses its reader and validator. Do not add a second entrypoint for an operation that already has one unless it carries clear operator value — duplicate entrypoints drift apart and it stops being obvious which is authoritative. --- ## 2. Root `Taskfile.yml` include ```yaml includes: module-example-v1: ./.scripts/module-example-v1/Taskfile.yml ``` Rules: 1. Add a root include only for a module intended as a **stable public task namespace**. The include name becomes a permanent user-facing prefix; renaming it later breaks every documented command and every CI invocation. 2. Choose a short, clear namespace. Existing examples include `ca`, `kube`, `npmrc`, `repos-local`, and `repos-local-nodejs`. 3. Do not add includes for experimental or in-progress modules — run them through their `api/` path until the interface settles. --- ## 3. Verifying ```bash # Every included namespace and task, with descriptions task --list-all # Confirm a specific task resolves and forwards arguments task : -- --dry-run ``` `task --list` shows only tasks carrying a `desc:`; `task --list-all` shows everything. A task missing from `--list-all` means the root include is absent or its path is wrong.