From 9c835c885b3908281a6351a0fdac4d17830adc89 Mon Sep 17 00:00:00 2001 From: Tigran Muradyan Date: Sun, 28 Jun 2026 12:02:00 +0400 Subject: [PATCH] feat(DMVP-9998): add multi-driver meta-cli with yaml validation and driver wrappers Support terramate/terragrunt/terraform-cloud init and lifecycle commands, validate-yaml for shared-config and linking checks, backend bootstrap helpers, IDE schema setup, and examples aligned with published driver modules. Co-authored-by: Cursor --- .github/workflows/npm-publish.yml | 9 +- .github/workflows/test.yml | 23 + .gitignore | 10 +- README.md | 653 +++++++++++++++++- bin/run.js | 22 +- examples/README.md | 24 + examples/terraform-cloud/.gitignore | 5 + examples/terraform-cloud/README.md | 35 + examples/terraform-cloud/advanced/_.yaml | 2 + .../advanced/group-0/module-a.yaml | 4 + .../advanced/group-0/module-b.yaml | 7 + .../terraform-cloud/advanced/metacloud.yaml | 6 + examples/terraform-cloud/basic/_.yaml | 2 + .../basic/group-0/module-a.yaml | 2 + examples/terraform-cloud/basic/metacloud.yaml | 6 + examples/terragrunt/.gitignore | 8 + examples/terragrunt/README.md | 46 ++ .../terragrunt/basic-azure-backend/_.yaml | 2 + .../basic-azure-backend/group-0/module-a.yaml | 4 + .../basic-azure-backend/group-0/module-b.yaml | 7 + .../basic-azure-backend/group-1/module-c.yaml | 8 + .../basic-azure-backend/metacloud.yaml | 9 + examples/terragrunt/basic-gcs-backend/_.yaml | 2 + .../basic-gcs-backend/group-0/module-a.yaml | 4 + .../basic-gcs-backend/group-0/module-b.yaml | 7 + .../basic-gcs-backend/group-1/module-c.yaml | 8 + .../basic-gcs-backend/metacloud.yaml | 7 + .../terragrunt/basic-gitlab-backend/_.yaml | 2 + .../group-0/module-a.yaml | 4 + .../group-0/module-b.yaml | 7 + .../group-1/module-c.yaml | 8 + .../basic-gitlab-backend/metacloud.yaml | 13 + .../terragrunt/basic-local-backend/_.yaml | 2 + .../basic-local-backend/group-0/module-a.yaml | 4 + .../basic-local-backend/group-0/module-b.yaml | 5 + .../basic-local-backend/group-1/module-c.yaml | 5 + .../basic-local-backend/metacloud.yaml | 6 + examples/terragrunt/basic-s3-backend/_.yaml | 2 + .../basic-s3-backend/group-0/module-a.yaml | 4 + .../basic-s3-backend/group-0/module-b.yaml | 7 + .../basic-s3-backend/group-1/module-c.yaml | 8 + .../basic-s3-backend/metacloud.yaml | 8 + examples/terramate/.gitignore | 6 + examples/terramate/README.md | 40 ++ examples/terramate/basic-azure-backend/_.yaml | 2 + .../basic-azure-backend/group-0/module-a.yaml | 4 + .../basic-azure-backend/group-0/module-b.yaml | 5 + .../basic-azure-backend/group-1/module-c.yaml | 5 + .../basic-azure-backend/metacloud.yaml | 11 + examples/terramate/basic-gcs-backend/_.yaml | 2 + .../basic-gcs-backend/group-0/module-a.yaml | 4 + .../basic-gcs-backend/group-0/module-b.yaml | 5 + .../basic-gcs-backend/group-1/module-c.yaml | 5 + .../basic-gcs-backend/metacloud.yaml | 9 + .../terramate/basic-gitlab-backend/_.yaml | 2 + .../group-0/module-a.yaml | 4 + .../group-0/module-b.yaml | 5 + .../group-1/module-c.yaml | 5 + .../basic-gitlab-backend/metacloud.yaml | 14 + examples/terramate/basic-local-backend/_.yaml | 2 + .../basic-local-backend/group-0/module-a.yaml | 4 + .../basic-local-backend/group-0/module-b.yaml | 5 + .../basic-local-backend/group-1/module-c.yaml | 5 + .../basic-local-backend/metacloud.yaml | 8 + examples/terramate/basic-s3-backend/_.yaml | 2 + .../basic-s3-backend/group-0/module-a.yaml | 4 + .../basic-s3-backend/group-0/module-b.yaml | 5 + .../basic-s3-backend/group-1/module-c.yaml | 5 + .../terramate/basic-s3-backend/metacloud.yaml | 10 + package.json | 19 +- schemas/metacloud/metacloud.schema.json | 64 ++ schemas/metacloud/shared-anchors.schema.json | 8 + schemas/metacloud/workspace.schema.json | 90 +++ scripts/autocomplete-setup.js | 182 +++++ scripts/postinstall.js | 11 + specs/001-multi-driver-init/plan.md | 501 ++++++++++++++ specs/001-multi-driver-init/spec.md | 330 +++++++++ specs/001-multi-driver-init/tasks.md | 38 + specs/002-driver-wrapper-commands/plan.md | 84 +++ specs/002-driver-wrapper-commands/spec.md | 219 ++++++ specs/002-driver-wrapper-commands/tasks.md | 39 ++ src/backend-bootstrap/azurerm.ts | 111 +++ src/backend-bootstrap/gcs.ts | 59 ++ src/backend-bootstrap/index.ts | 169 +++++ src/backend-bootstrap/runner.ts | 117 ++++ src/backend-bootstrap/s3.ts | 73 ++ src/backend-bootstrap/terraform-cloud.ts | 124 ++++ src/backend-bootstrap/types.ts | 43 ++ src/command-groups.ts | 30 + src/commands/apply.ts | 62 ++ src/commands/auth.ts | 2 + src/commands/configure.ts | 2 + src/commands/destroy.ts | 62 ++ src/commands/exec.ts | 2 + src/commands/init.ts | 336 ++++++--- src/commands/list.ts | 42 ++ src/commands/open.ts | 2 + src/commands/plan.ts | 62 ++ src/commands/refresh.ts | 2 + src/commands/scan.ts | 2 + src/commands/tf-bootstrap-backend.ts | 89 +++ src/commands/tfa.ts | 24 + src/commands/tfd.ts | 24 + src/commands/tfi.ts | 24 + src/commands/tfp.ts | 24 + src/commands/validate-yaml.ts | 74 ++ src/commands/validate.ts | 59 ++ src/driver-command-flags.ts | 26 + src/driver-runtime.ts | 540 +++++++++++++++ src/help.ts | 78 +++ src/ide-schemas.ts | 175 +++++ src/setup-display.ts | 32 + src/terraform-workspace.ts | 87 +++ src/utils.ts | 319 ++++++--- src/workspace-context.ts | 252 +++++++ src/yaml-validation.ts | 568 +++++++++++++++ test/backend-bootstrap.test.ts | 120 ++++ test/command-groups.test.ts | 34 + test/commands/auth.test.ts | 17 - test/commands/configure.test.ts | 17 - test/commands/exec.test.ts | 17 - test/commands/hello/index.test.ts | 10 - test/commands/hello/world.test.ts | 10 - test/commands/open.test.ts | 17 - test/driver-runtime.test.ts | 255 +++++++ test/examples.test.ts | 198 ++++++ test/ide-schemas.test.ts | 93 +++ test/setup-display.test.ts | 36 + test/terraform-workspace.test.ts | 75 ++ test/utils.test.ts | 166 +++++ test/validate-yaml.test.ts | 189 +++++ test/workspace-context.test.ts | 72 ++ 132 files changed, 7507 insertions(+), 286 deletions(-) create mode 100644 .github/workflows/test.yml create mode 100644 examples/README.md create mode 100644 examples/terraform-cloud/.gitignore create mode 100644 examples/terraform-cloud/README.md create mode 100644 examples/terraform-cloud/advanced/_.yaml create mode 100644 examples/terraform-cloud/advanced/group-0/module-a.yaml create mode 100644 examples/terraform-cloud/advanced/group-0/module-b.yaml create mode 100644 examples/terraform-cloud/advanced/metacloud.yaml create mode 100644 examples/terraform-cloud/basic/_.yaml create mode 100644 examples/terraform-cloud/basic/group-0/module-a.yaml create mode 100644 examples/terraform-cloud/basic/metacloud.yaml create mode 100644 examples/terragrunt/.gitignore create mode 100644 examples/terragrunt/README.md create mode 100644 examples/terragrunt/basic-azure-backend/_.yaml create mode 100644 examples/terragrunt/basic-azure-backend/group-0/module-a.yaml create mode 100644 examples/terragrunt/basic-azure-backend/group-0/module-b.yaml create mode 100644 examples/terragrunt/basic-azure-backend/group-1/module-c.yaml create mode 100644 examples/terragrunt/basic-azure-backend/metacloud.yaml create mode 100644 examples/terragrunt/basic-gcs-backend/_.yaml create mode 100644 examples/terragrunt/basic-gcs-backend/group-0/module-a.yaml create mode 100644 examples/terragrunt/basic-gcs-backend/group-0/module-b.yaml create mode 100644 examples/terragrunt/basic-gcs-backend/group-1/module-c.yaml create mode 100644 examples/terragrunt/basic-gcs-backend/metacloud.yaml create mode 100644 examples/terragrunt/basic-gitlab-backend/_.yaml create mode 100644 examples/terragrunt/basic-gitlab-backend/group-0/module-a.yaml create mode 100644 examples/terragrunt/basic-gitlab-backend/group-0/module-b.yaml create mode 100644 examples/terragrunt/basic-gitlab-backend/group-1/module-c.yaml create mode 100644 examples/terragrunt/basic-gitlab-backend/metacloud.yaml create mode 100644 examples/terragrunt/basic-local-backend/_.yaml create mode 100644 examples/terragrunt/basic-local-backend/group-0/module-a.yaml create mode 100644 examples/terragrunt/basic-local-backend/group-0/module-b.yaml create mode 100644 examples/terragrunt/basic-local-backend/group-1/module-c.yaml create mode 100644 examples/terragrunt/basic-local-backend/metacloud.yaml create mode 100644 examples/terragrunt/basic-s3-backend/_.yaml create mode 100644 examples/terragrunt/basic-s3-backend/group-0/module-a.yaml create mode 100644 examples/terragrunt/basic-s3-backend/group-0/module-b.yaml create mode 100644 examples/terragrunt/basic-s3-backend/group-1/module-c.yaml create mode 100644 examples/terragrunt/basic-s3-backend/metacloud.yaml create mode 100644 examples/terramate/.gitignore create mode 100644 examples/terramate/README.md create mode 100644 examples/terramate/basic-azure-backend/_.yaml create mode 100644 examples/terramate/basic-azure-backend/group-0/module-a.yaml create mode 100644 examples/terramate/basic-azure-backend/group-0/module-b.yaml create mode 100644 examples/terramate/basic-azure-backend/group-1/module-c.yaml create mode 100644 examples/terramate/basic-azure-backend/metacloud.yaml create mode 100644 examples/terramate/basic-gcs-backend/_.yaml create mode 100644 examples/terramate/basic-gcs-backend/group-0/module-a.yaml create mode 100644 examples/terramate/basic-gcs-backend/group-0/module-b.yaml create mode 100644 examples/terramate/basic-gcs-backend/group-1/module-c.yaml create mode 100644 examples/terramate/basic-gcs-backend/metacloud.yaml create mode 100644 examples/terramate/basic-gitlab-backend/_.yaml create mode 100644 examples/terramate/basic-gitlab-backend/group-0/module-a.yaml create mode 100644 examples/terramate/basic-gitlab-backend/group-0/module-b.yaml create mode 100644 examples/terramate/basic-gitlab-backend/group-1/module-c.yaml create mode 100644 examples/terramate/basic-gitlab-backend/metacloud.yaml create mode 100644 examples/terramate/basic-local-backend/_.yaml create mode 100644 examples/terramate/basic-local-backend/group-0/module-a.yaml create mode 100644 examples/terramate/basic-local-backend/group-0/module-b.yaml create mode 100644 examples/terramate/basic-local-backend/group-1/module-c.yaml create mode 100644 examples/terramate/basic-local-backend/metacloud.yaml create mode 100644 examples/terramate/basic-s3-backend/_.yaml create mode 100644 examples/terramate/basic-s3-backend/group-0/module-a.yaml create mode 100644 examples/terramate/basic-s3-backend/group-0/module-b.yaml create mode 100644 examples/terramate/basic-s3-backend/group-1/module-c.yaml create mode 100644 examples/terramate/basic-s3-backend/metacloud.yaml create mode 100644 schemas/metacloud/metacloud.schema.json create mode 100644 schemas/metacloud/shared-anchors.schema.json create mode 100644 schemas/metacloud/workspace.schema.json create mode 100644 scripts/autocomplete-setup.js create mode 100644 scripts/postinstall.js create mode 100644 specs/001-multi-driver-init/plan.md create mode 100644 specs/001-multi-driver-init/spec.md create mode 100644 specs/001-multi-driver-init/tasks.md create mode 100644 specs/002-driver-wrapper-commands/plan.md create mode 100644 specs/002-driver-wrapper-commands/spec.md create mode 100644 specs/002-driver-wrapper-commands/tasks.md create mode 100644 src/backend-bootstrap/azurerm.ts create mode 100644 src/backend-bootstrap/gcs.ts create mode 100644 src/backend-bootstrap/index.ts create mode 100644 src/backend-bootstrap/runner.ts create mode 100644 src/backend-bootstrap/s3.ts create mode 100644 src/backend-bootstrap/terraform-cloud.ts create mode 100644 src/backend-bootstrap/types.ts create mode 100644 src/command-groups.ts create mode 100644 src/commands/apply.ts create mode 100644 src/commands/destroy.ts create mode 100644 src/commands/list.ts create mode 100644 src/commands/plan.ts create mode 100644 src/commands/tf-bootstrap-backend.ts create mode 100644 src/commands/tfa.ts create mode 100644 src/commands/tfd.ts create mode 100644 src/commands/tfi.ts create mode 100644 src/commands/tfp.ts create mode 100644 src/commands/validate-yaml.ts create mode 100644 src/commands/validate.ts create mode 100644 src/driver-command-flags.ts create mode 100644 src/driver-runtime.ts create mode 100644 src/help.ts create mode 100644 src/ide-schemas.ts create mode 100644 src/setup-display.ts create mode 100644 src/terraform-workspace.ts create mode 100644 src/workspace-context.ts create mode 100644 src/yaml-validation.ts create mode 100644 test/backend-bootstrap.test.ts create mode 100644 test/command-groups.test.ts delete mode 100644 test/commands/auth.test.ts delete mode 100644 test/commands/configure.test.ts delete mode 100644 test/commands/exec.test.ts delete mode 100644 test/commands/hello/index.test.ts delete mode 100644 test/commands/hello/world.test.ts delete mode 100644 test/commands/open.test.ts create mode 100644 test/driver-runtime.test.ts create mode 100644 test/examples.test.ts create mode 100644 test/ide-schemas.test.ts create mode 100644 test/setup-display.test.ts create mode 100644 test/terraform-workspace.test.ts create mode 100644 test/utils.test.ts create mode 100644 test/validate-yaml.test.ts create mode 100644 test/workspace-context.test.ts diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 488a7a9..6eecc4a 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -11,11 +11,12 @@ jobs: publish-npm: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: - node-version: 18 + node-version: 22 registry-url: https://registry.npmjs.org/ + cache: yarn - name: Update package.json version run: | # Extract version from tag and update package.json @@ -23,6 +24,8 @@ jobs: jq ".version=\"$VERSION\"" package.json > package.tmp.json mv package.tmp.json package.json - run: yarn install --frozen-lockfile + - run: yarn build + - run: yarn test - run: yarn publish --access=public env: NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..e539273 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,23 @@ +name: Test + +on: + push: + branches: [main] + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + node-version: [18, 20, 22] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: yarn + - run: yarn install --frozen-lockfile + - run: yarn build + - run: yarn test diff --git a/.gitignore b/.gitignore index 5ac9465..52c2616 100644 --- a/.gitignore +++ b/.gitignore @@ -7,8 +7,16 @@ /lib /package-lock.json /tmp +test/.tmp-ide-schemas node_modules oclif.manifest.json +*.tgz +# Generated by `meta init` — examples/ keeps committed reference copies +metacloud.yaml +!examples/**/metacloud.yaml _metacloud.tf -metacloud.yaml \ No newline at end of file + +# Local Terraform state from workspace runs (e.g. meta tfa on _metacloud.tf) +terraform.tfstate +terraform.tfstate.* diff --git a/README.md b/README.md index ad98a5b..d12ce90 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,83 @@ -oclif-hello-world +@dasmeta/meta-cli ================= -oclif example Hello World CLI +CLI tool for DasMeta infrastructure workflows. `meta init` bootstraps generated infrastructure handlers (`metacloud.yaml`, `_metacloud.tf`). `meta exec` opens an AWS-authenticated shell — **required for the `terraform-cloud` driver**, optional for other drivers depending on backend/credentials. -[![oclif](https://img.shields.io/badge/cli-oclif-brightgreen.svg)](https://oclif.io) -[![CircleCI](https://circleci.com/gh/oclif/hello-world/tree/main.svg?style=shield)](https://circleci.com/gh/oclif/hello-world/tree/main) -[![GitHub license](https://img.shields.io/github/license/oclif/hello-world)](https://github.com/oclif/hello-world/blob/main/LICENSE) +`meta init` now supports multiple drivers: +- `terraform-cloud` +- `terramate` +- `terragrunt` + +Wrapper commands are also available: +- `meta validate` +- `meta plan` +- `meta apply` +- `meta destroy` + +Backward compatibility is preserved: +- if `metacloud.yaml` has no `driver`, `meta` treats it as `terraform-cloud` +- first-time `meta init` without `--driver` still defaults to `terraform-cloud` +* [Prerequisites](#prerequisites) +* [Supported Node.js versions](#supported-nodejs-versions) * [Usage](#usage) +* [Init Workflow](#init-workflow) +* [metacloud.yaml Examples](#metacloudyaml-examples) +* [Wrapper Commands](#wrapper-commands) * [Commands](#commands) +# Prerequisites + +Install via npm (global): + +```bash +npm install -g @dasmeta/meta-cli +``` + +# Supported Node.js versions + +`meta` requires a supported Node.js runtime. The requirement is declared in `package.json`: + +```json +"engines": { "node": ">=18" } +``` + +| Node.js | Status | Notes | +|---------|--------|-------| +| **18.x** | Supported (LTS) | CI-tested on every push/PR | +| **20.x** | Supported (LTS) | CI-tested on every push/PR | +| **22.x** | Supported (LTS) | CI-tested on every push/PR | +| **24.x+** | Best effort | Not in CI matrix yet; install/`meta --help` smoke-tested in Docker | +| **16.x and below** | Not supported | Below `engines.node`; may fail at install or runtime | + +**Patch versions:** any patch within a supported major is fine (e.g. `18.20.4`, `20.16.0`, `22.5.1`). No exact Node patch is required. + +**npm:** use npm 9+ (bundled with Node 18+). On Node 24 / npm 11, lifecycle scripts may require script approval; autocomplete still runs on the first `meta` command if postinstall is skipped. + +## Shell autocomplete + +Shell tab completion is configured automatically on global install: +- builds the autocomplete cache during `postinstall` +- appends the shell hook to `~/.zshrc` or `~/.bashrc` when missing (interactive installs only) + +If npm skips lifecycle scripts (npm 11+ `allow-scripts`), the cache is built on the first `meta` run instead. + +Opt out: `META_SKIP_AUTOCOMPLETE=1 npm install -g @dasmeta/meta-cli` + +Manual refresh or re-print setup: `meta autocomplete` + +## Local development + +For local development from this repository: + +```bash +yarn install +yarn build +./bin/dev.js --help # development entrypoint (TypeScript) +./bin/run.js --help # production entrypoint (compiled dist/) +``` + # Usage ```sh-session @@ -18,21 +85,268 @@ $ npm install -g @dasmeta/meta-cli $ meta COMMAND running command... $ meta (--version) -@dasmeta/meta-cli/0.0.0 darwin-arm64 node-v18.19.0 +@dasmeta/meta-cli/0.0.0 darwin-arm64 node-v22.5.1 $ meta --help [COMMAND] USAGE $ meta COMMAND ... ``` +# Init Workflow + +## When `meta exec` is required + +| Driver | `meta exec` before `meta init`? | Why | +|--------|----------------------------------|-----| +| `terraform-cloud` | **Yes** | Needs an active client session (`META_CLIENT_NAME`), AWS env, and 1Password-backed Terraform Cloud / Git tokens | +| `terramate` | **No** (when `metacloud.yaml` exists) | Run `meta init` directly to regenerate `_metacloud.tf` from `metacloud.yaml` | +| `terragrunt` | **No** (when `metacloud.yaml` exists) | Same as Terramate — no `meta exec` for init/regenerate | + +For `terramate` / `terragrunt`, use `meta exec` only when you need AWS (or other cloud) credentials for remote backends or for `meta plan` / `meta apply` / `meta destroy`. + +## First-time setup + +### Terraform Cloud + +```bash +meta exec # required first +meta init # or: meta init --driver terraform-cloud +``` + +Inside the `meta exec` shell, `meta init` prompts for Terraform Cloud org/workspace, Git metadata, and fetches TFC/Git tokens from 1Password. + +### Terramate / Terragrunt + +When `metacloud.yaml` is already in the repo (typical for driver repos): + +```bash +meta init # regenerates _metacloud.tf from metacloud.yaml +# or locally from meta-cli source: +meta-dev init --driver terramate +``` + +No `meta exec` session is required for this regenerate flow. + +First-time creation of `metacloud.yaml` (no file yet) still runs inside a shell with client context if you use the interactive `meta init` prompts; prefer committing a starter `metacloud.yaml` for Terramate/Terragrunt repos (see [examples/](./examples/)). + +## Outputs + +`meta init` generates or refreshes: + +- `metacloud.yaml` +- `_metacloud.tf` +- `.vscode/settings.json` YAML schema mappings (VS Code / Cursor; see [IDE YAML schemas](#ide-yaml-schemas)) + +On later runs, `meta init` reads `metacloud.yaml` and regenerates `_metacloud.tf` from it. + +## IDE YAML schemas + +MetaCloud JSON schemas ship inside `@dasmeta/meta-cli` under `schemas/metacloud/`. They are also available from npm via unpkg, for example: + +`https://unpkg.com/@dasmeta/meta-cli@/schemas/metacloud/workspace.schema.json` + +On every `meta init`, meta-cli merges `yaml.schemas` into the nearest `.vscode/settings.json` (walking up to the git root). By default it references the published unpkg URLs for the installed package version, so you do not need to copy schema files into your repo. + +```bash +meta init # remote schemas (default after publish) +meta init --ide-schemas local # copy bundled schemas into .vscode/schemas/metacloud +meta init --skip-ide-schemas # skip IDE setup +``` + +Requires the [Red Hat YAML extension](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml) in VS Code or Cursor. + +## Examples + +```bash +meta init +meta init --driver terraform-cloud +meta init --driver terramate +meta init --driver terragrunt +``` + +Behavior: +- if `metacloud.yaml` exists and `--force` is not used, the driver from the file is used +- if `metacloud.yaml` exists but has no `driver`, `terraform-cloud` is assumed +- if `--force` is used, `meta init` prompts again and rewrites `metacloud.yaml` + +Driver-specific notes: +- `terraform-cloud` + - **requires `meta exec`** before init + - requires `git_provider`, `git_org`, and `git_repo` in `metacloud.yaml` (used by `dasmeta/cloud/tfe` for VCS-linked modules) + - keeps the Terraform Cloud and 1Password token flow + - fetches Terraform Cloud and Git tokens from 1Password +- `terramate` + - **does not require `meta exec` for init** when `metacloud.yaml` exists + - `git_*` fields are not used — omit them + - uses a shared `terraform_backend` block from `metacloud.yaml` + - defaults generated stacks to `_terraform` + - defaults `linking_mode` to `terramate_outputs_sharing` + - defaults `mock_inputs_enabled` to `true` +- `terragrunt` + - **does not require `meta exec` for init** when `metacloud.yaml` exists + - `git_*` fields are not used — omit them + - uses a shared `terraform_backend` block from `metacloud.yaml` + - defaults generated units to `_terragrunt` + +AWS environment: +- `meta exec` provides AWS credentials and SSO session environment (required for `terraform-cloud` init; optional for Terramate/Terragrunt depending on backend) +- Terramate and Terragrunt do not require new 1Password secrets by default + +# metacloud.yaml Examples + +Full minimal layouts per driver live in [examples/](./examples/). + +Terraform Cloud: + +```yaml +driver: terraform-cloud +terraform_cloud_org: dasmeta +terraform_cloud_workspace: infrastructure +git_provider: github +git_org: dasmeta +git_repo: infrastructure +``` + +Terramate: + +```yaml +driver: terramate +yaml_dir: . +target_dir: _terraform +terraform_backend: + name: s3 + configs: + bucket: my-state-bucket + region: eu-central-1 + key: terramate +linking_mode: terramate_outputs_sharing +mock_inputs_enabled: true +stack_id_prefix: null +``` + +Terragrunt: + +```yaml +driver: terragrunt +yaml_dir: . +target_dir: _terragrunt +terraform_backend: + name: s3 + configs: + bucket: my-state-bucket + region: eu-central-1 + key: terragrunt +``` + +Generated `_metacloud.tf` shape: +- Terraform Cloud renders `terraform { cloud { ... } }` plus `dasmeta/cloud/tfe` +- Terramate renders root `terraform { backend ... }` (bootstrap state) plus `dasmeta/cli/terramate` +- Terragrunt renders root `terraform { backend ... }` (bootstrap state) plus `dasmeta/cli/terragrunt` + +For Terramate and Terragrunt, `terraform_backend` in `metacloud.yaml` is used twice: +1. root `terraform { backend ... }` block — state for the driver workspace itself +2. `module "metacloud" { terraform_backend = ... }` — default backend for generated child stacks + +Common Terramate and Terragrunt backend guidance: +- use one shared `terraform_backend` block in `metacloud.yaml` +- use `s3` for real shared state +- use `local` only for local testing + +## Bootstrap backend resources + +`meta tf-bootstrap-backend` creates the remote backend object described in `metacloud.yaml`. It prints the provider CLI commands that will run, waits for confirmation, then provisions resources: + +| Driver / backend | Tool | Creates | +|------------------|------|---------| +| `terraform-cloud` | Terraform Cloud API (`curl` / axios) | Workspace with **local execution mode** (CLI-driven runs, remote state) | +| `s3` | AWS CLI | Versioned, encrypted S3 bucket with public access blocked | +| `azurerm` | Azure CLI | Resource group, storage account, blob container | +| `gcs` | gcloud CLI | GCS bucket with versioning | + +```bash +cd terramate/ # directory with metacloud.yaml +meta tf-bootstrap-backend +meta tf-bootstrap-backend --yes # skip confirmation +meta tf-bootstrap-backend --dir ./terramate --location westeurope +``` + +Requirements: +- `metacloud.yaml` must already define the target backend (`terraform_backend` or `terraform_cloud_*`) +- Cloud credentials must be available in the shell (`aws`, `az`, or `gcloud` logged in; `TF_TOKEN_app_terraform_io` or `meta exec` + 1Password for Terraform Cloud) +- Existing resources are detected and skipped (idempotent) + +# Wrapper Commands + +`meta validate`, `meta plan`, `meta apply`, and `meta destroy` read `metacloud.yaml`, detect the configured driver, and run the appropriate underlying tool with driver-specific defaults. + +Common flags: +- `--dir ` + - override the generated driver directory +- `--setup ` + - target one or more setups by exact relative path or exact basename + - supports repeated flags and comma-separated values +- `--` + - pass raw extra arguments through to the underlying tool + +Examples: + +```bash +meta validate +meta plan --setup group-0/module-a +meta apply --setup group-0/module-a,group-2/dns-zone +meta destroy --setup group-3/dns-records --setup group-2/dns-zone +meta plan -- --lock-timeout=5m +meta apply --dir custom-output -- -parallelism=20 +``` + +Setup selector rules: +- exact relative path matches are preferred +- exact basenames are allowed only when they are unique +- ambiguous basenames fail and require a path-form selector + +Driver behavior: +- `terraform-cloud` + - operates per generated Terraform setup directory + - `validate` tries `terraform validate` first and runs `terraform init -backend=false` only if the error indicates init is required + - `plan`, `apply`, and `destroy` try the requested action first and run `terraform init` only if the error indicates init is required +- `terramate` + - `validate` runs: + - `terramate generate` + - `terramate run --enable-sharing --mock-on-fail --disable-safeguards=git-untracked,git-uncommitted -- terraform validate` + - if init is required, it runs `terramate run -- terraform init` and retries once + - `plan` and `apply` use: + - `--enable-sharing` + - `--mock-on-fail` + - `--disable-safeguards=git-untracked,git-uncommitted` + - and run `terramate run -- terraform init` only when the first failure indicates init is required + - `destroy` uses: + - `--enable-sharing` + - `--disable-safeguards=git-untracked,git-uncommitted` + - `--reverse` + - and runs `terramate run -- terraform init` only when the first failure indicates init is required + - `apply` and `destroy` remain interactive by default +- `terragrunt` + - when no `--setup` is provided, runs `terragrunt run --all` + - `validate` tries `validate` first and runs `init -backend=false` only if the error indicates init is required + - `apply` and `destroy` use `--no-auto-approve` when running with `--all` + - `plan`, `apply`, and `destroy` run `init` only when the first failure indicates init is required + +Missing tool handling: +- if a required executable is missing, the wrapper stops early +- the error includes quick install commands for macOS and Linux + # Commands +* [`meta apply`](#meta-apply) * [`meta auth`](#meta-auth) * [`meta configure`](#meta-configure) +* [`meta destroy`](#meta-destroy) * [`meta exec ACCOUNT ENV`](#meta-exec-account-env) * [`meta help [COMMANDS]`](#meta-help-commands) +* [`meta list`](#meta-list) * [`meta init`](#meta-init) * [`meta open ACCOUNT ENV`](#meta-open-account-env) +* [`meta plan`](#meta-plan) * [`meta plugins`](#meta-plugins) * [`meta plugins:install PLUGIN...`](#meta-pluginsinstall-plugin) * [`meta plugins:inspect PLUGIN...`](#meta-pluginsinspect-plugin) @@ -45,16 +359,55 @@ USAGE * [`meta plugins update`](#meta-plugins-update) * [`meta refresh`](#meta-refresh) * [`meta scan`](#meta-scan) +* [`meta tfa`](#meta-tfa) +* [`meta tfd`](#meta-tfd) +* [`meta tfi`](#meta-tfi) +* [`meta tfp`](#meta-tfp) +* [`meta validate`](#meta-validate) +* [`meta validate-yaml`](#meta-validate-yaml) + +## `meta apply` + +Apply generated setups via the configured driver + +``` +USAGE + $ meta apply [--dir ] [--driver terraform-cloud|terramate|terragrunt] [--setup ] + +FLAGS + --dir= Override the generated driver directory + --driver=