Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions .slim/codemap.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{
"metadata": {
"version": "1.0.0",
"last_run": "2026-07-25T14:49:48.247Z",
"root": "/Users/maxwellmapako/Git/stackctl",
"include_patterns": [
"src/**/*.ts",
"deno.json",
"renovate.json"
],
"exclude_patterns": [
"**/*_test.ts",
"**/*.test.ts",
"**/*.spec.ts",
"tests/**",
"__tests__/**",
"docs/**",
"*.md",
"LICENSE",
"dist/**",
"build/**",
"node_modules/**",
".coverage/**"
],
"exceptions": []
},
"file_hashes": {
"deno.json": "5fe7bb0a193c73ba9ab2d7c39a36d2ed",
"renovate.json": "7c9b928b8987124bed6b28883a9687e6",
"src/cli/mod.ts": "00243b0de688f250042b16fee4872c2d",
"src/compose/discover.ts": "940e5f38d0e0f6312bd61b7e968569f4",
"src/compose/generate.ts": "6209433dc425bbe5103fc152ff3cc119",
"src/compose/load.ts": "12b52236167bf99c3e155c384e6549ca",
"src/compose/merge.ts": "b3cd7d2bf92fc1d093596dc8eb7f71db",
"src/compose/mod.ts": "0c83c55a4e4d4e4d591c451d4fff9773",
"src/compose/override.ts": "d60fb396d29a15313d413180f47476c1",
"src/compose/plan.ts": "b2784452e655fe500562b128871ca2b6",
"src/compose/reload.ts": "af2b11dffb4f110e4053cabe6ac5d075",
"src/compose/sync.ts": "ed5c67440bc11a054dc0c4dfe42b5dc2",
"src/compose/transform.ts": "1fdf5d424493b304bcd849e9d0836ed2",
"src/compose/types.ts": "c8b38efa061342ae32904beb0ba7dfa9",
"src/compose/volumes.ts": "967d238507521e085767e5d1fb9ea4fd",
"src/config/defaults.ts": "c8f3cba23f87b199c58617e473431dfd",
"src/config/init.ts": "0cf38639c0f954dd3255cf640b92e394",
"src/config/load.ts": "9171d47d97f1a8c8c0cb547428848e9d",
"src/config/merge.ts": "11e69aa61ad26c71937b46fbc6b06bf0",
"src/config/mod.ts": "aea05aaffc63bb5948525c23d3b29cba",
"src/config/types.ts": "53c49bfaa0a981d3e99ebd57bced12a0",
"src/config/validate.ts": "3912273078fd61ce3e5d8d3942b8d0ed",
"src/docker/mod.ts": "40ffa338a149cc954825c296fbfa24dd",
"src/env/mod.ts": "bc28cb84e7c17e3e7c18c51999e901f2",
"src/env/types.ts": "cf911306a70bee066ecd94b3246d1d96",
"src/main.ts": "49922cd2ec13833d914201fbd00059fd",
"src/process/runner.ts": "69b2c4983181e12ce6fd3c1150e0ff9e",
"src/process/types.ts": "26ead4ac38df3dc0cfd6f9222e3b6c6f",
"src/render/mod.ts": "cbf8cf6dd400c8a499e69bf57febd18b",
"src/secrets/index.ts": "42a570bfd29a052876d235859adea0e9",
"src/secrets/mod.ts": "7fc2c19b3e98e5cd4a4aadea01c4c15b",
"src/secrets/types.ts": "4b9870e0afe18fdafc0d237949d4e4c7",
"src/testing/fakes.ts": "155670849b385ad66a3d7868752e7b51",
"src/testing/mod.ts": "91a805e7ae874928e0d88028a40f617d",
"src/version.ts": "d12cfc326f43822ca2fcd782ff390c2e"
},
"folder_hashes": {
".": "5cfbcb64d0082c6c3fe58ded5870c051",
"src": "d8c5df30952b1cbb7286b0f8750e222f",
"src/cli": "d4fe4219490373f57c8def4ad6d32c66",
"src/compose": "079b1338a0d6366f0c1aa99435a5c8ed",
"src/config": "1c9c83c1f66720a8de640711cdc1e084",
"src/docker": "9e79906bb316e466910ea06715c45ba5",
"src/env": "a6747b7e75c24b4c9a95c3889e875c8b",
"src/process": "73b6d490c2df5f37f309c542b2b26cbb",
"src/render": "2ffa53736bca6abd8de0d145e4929036",
"src/secrets": "8055ab3d61611a7eca16d35b20ad34e7",
"src/testing": "ebdfc0eda40532a85abb455d21fa416d"
}
}
12 changes: 12 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,15 @@ If no issue number exists, use `<type>/<short-description>` and link to an issue
- This project is in **early development**. All commands are listed as "Planned" in `README.md`; check actual implementation status in `src/cli/mod.ts` before assuming a command exists.
- Permissions for the compiled binary are defined in `src/main.ts` shebang and the CI build step. Do not change permissions without updating both.
- Git-ignored files: `.stackctl.local`, `.stackctl.local.*`, `*.env` (except `.env.example`), `*.env.enc`, `age-key.txt`, `age.key`, `dist/`, `.rendered/`, `.coverage/`, `cov/`.

## Repository Map

A full codemap is available at `codemap.md` in the project root.

Before working on any task, read `codemap.md` to understand:

- Project architecture and entry points
- Directory responsibilities and design patterns
- Data flow and integration points between modules

For deep work on a specific folder, also read that folder's `codemap.md`.
93 changes: 93 additions & 0 deletions codemap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Repository Atlas: stackctl

## Project Responsibility

`stackctl` is a Deno 2.x command line application for managing Docker Compose based stacks across
multi-service repositories. It resolves layered `.stackctl` configuration, discovers service compose
files, generates canonical Docker Swarm stack YAML, renders environment substitutions, manages SOPS
and age encrypted dotenv files, and delegates Docker operations through a typed process execution
boundary.

The package is published as `@anitrend/stackctl` and exports `src/main.ts` as the executable entry
point.

## System Entry Points

- `src/main.ts`: Deno executable bootstrap. It passes `Deno.args` to the CLI module and exits with
the returned status code.
- `src/cli/mod.ts`: Cliffy command tree, option parsing, command orchestration, output formatting,
and exit-code mapping.
- `deno.json`: Package metadata, JSR export path, task definitions, formatter settings, lint rules,
compiler options, and JSR dependency map.
- `renovate.json`: Renovate dependency automation configuration, using `dependencies/` branches and
`main` as the base branch.
- `AGENTS.md`: Agent-facing repository instructions, runtime constraints, branch and commit
conventions, and the required codemap entry point.

## Root Asset Map

| Path | Responsibility |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `deno.json` | Defines Deno tasks, JSR imports, lint, format, strict compiler options, package name, version, and export target. |
| `deno.lock` | Locks JSR and standard library dependency versions for deterministic Deno resolution. |
| `README.md` | User-facing overview, install path, command status table, feature list, and development commands. |
| `CONTRIBUTING.md` | Contribution workflow, branch naming, commit format, and PR expectations. |
| `AGENTS.md` | Agent operating guide, source layout, runtime rules, and codemap discovery instructions. |
| `renovate.json` | Automated dependency update policy and branch prefix configuration. |
| `.github/` | CI, release, version bump, stale issue handling, release drafter, auto approval, and composite setup action workflows. |
| `.slim/codemap.json` | Codemap state file containing selected source hashes and folder hashes for change detection. |

## Repository Directory Map

| Directory | Responsibility Summary | Detailed Map |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------- |
| `src/` | Deno executable entry point and version module. It bootstraps the CLI and centralizes the release version constant. | [View Map](src/codemap.md) |
| `src/cli/` | Command registration, option parsing, runner injection, domain delegation, output formatting, and semantic exit-code adaptation. | [View Map](src/cli/codemap.md) |
| `src/config/` | Configuration shape, defaults, discovery, YAML loading, layered merging, validation, initialization, and public config exports. | [View Map](src/config/codemap.md) |
| `src/compose/` | Compose discovery, loading, generation, merging, overrides, Swarm transforms, named volumes, reload, sync, and operation planning. | [View Map](src/compose/codemap.md) |
| `src/render/` | Docker Compose interpolation, service environment scope construction, recursive substitution, path absolutization, and warning collection. | [View Map](src/render/codemap.md) |
| `src/docker/` | Typed Docker, Docker Compose, and Docker Swarm command wrappers built around the injected `ProcessRunner` boundary. | [View Map](src/docker/codemap.md) |
| `src/secrets/` | SOPS and age dotenv encryption, decryption materialization, secure cleanup, tooling checks, and deploy pipeline integration. | [View Map](src/secrets/codemap.md) |
| `src/env/` | Env example discovery, `.env` scaffolding, profile materialization, key diffing, status listing, and plaintext audit findings. | [View Map](src/env/codemap.md) |
| `src/process/` | External command execution abstraction, real Deno process adapter, dry-run behavior, output capture, streaming, and availability checks. | [View Map](src/process/codemap.md) |
| `src/testing/` | In-memory `ProcessRunner` test harness with configured responses, command recording, dry-run propagation, and assertion helpers. | [View Map](src/testing/codemap.md) |

## High-Level Data & Control Flow

1. Runtime starts at `src/main.ts`, which calls `main(Deno.args)` from `src/cli/mod.ts`.
2. The CLI builds a Cliffy command tree, parses user input, and selects a command action.
3. Config-aware commands call `resolveConfig` to merge defaults, base config, profile overlays,
local overlays, and explicit override files into a validated `ResolvedConfig`.
4. Compose workflows discover source compose files with `x-stack` metadata, load stack fragments,
merge compose data, apply override files, transform service definitions for Swarm compatibility,
collect named volumes, and serialize stack YAML.
5. Render workflows parse generated stack YAML, build service-specific interpolation scopes from the
shell environment, `env_file` values, and inline service environment values, then substitute
`${VAR}` and `$VAR` references.
6. Docker workflows pass generated or rendered compose files into Docker wrapper functions. Those
wrappers build explicit argument vectors and execute them through `ProcessRunner`.
7. Secrets workflows use the same process boundary to call SOPS, age, shred, rm, and Docker during
encryption, decryption, cleanup, and deploy operations.
8. Env workflows create or inspect plaintext `.env` artifacts that compose, render, and secrets
workflows consume by convention.

## Architectural Patterns

- Command adapter layer: `src/cli/` translates user input into typed calls across the domain
modules.
- Port and adapter boundary: `src/process/` defines `ProcessRunner`, while production uses
`RealProcessRunner` and tests use fakes from `src/testing/`.
- Functional transformation pipeline: compose and render modules prefer pure merge, transform, and
interpolation helpers that return new data structures.
- Layered configuration: config resolution applies deterministic file ordering and validates the
merged result before downstream modules consume it.
- Structured result envelopes: domain operations return warnings, errors, drift state, generated
content, and operation status as data so the CLI can decide presentation and exit codes.

## Verification Entry Points

- `deno task fmt:check`: Check formatting using the repository formatter settings.
- `deno task lint`: Run lint rules for `src/`.
- `deno task check`: Type-check `src/main.ts` with strict compiler options.
- `deno task test`: Run the unit test suite with required Deno permissions.
- `deno task coverage`: Generate a detailed coverage report after running coverage-enabled tests.
70 changes: 70 additions & 0 deletions src/cli/codemap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# src/cli/

## Responsibility

The CLI command layer exposes the `stackctl` executable interface. It builds the Cliffy command
tree, maps command-line options and positional arguments into typed operation inputs, invokes the
domain modules, formats human-readable or JSON output, and translates validation, dependency, and
unexpected failures into process exit codes.

## Design Patterns

- Command registration is centralized in `buildCli()`, which creates the root `Command`, attaches
global metadata and options, then registers each top-level command and nested command in one
command tree.
- Each command action is a thin adapter. It extracts options from `Record<string, unknown>`, casts
Cliffy parsed values to local types, normalizes comma-separated lists, resolves the active working
directory with `Deno.cwd()`, and delegates work to a module function.
- Runner injection is explicit at Docker and secrets boundaries. Command actions instantiate
`RealProcessRunner`, passing `dryRun` where supported, then provide that runner to Docker and
secrets operations instead of invoking external tools directly.
- Exit handling uses the shared `ExitCode` enum for semantic results. Many command actions set the
module-level `exitCode` and return, while some deeper asynchronous paths call `Deno.exit()` for
immediate termination.
- Output handling stays in the CLI layer. Domain results return structured data, warnings, errors,
and generated content. The CLI decides whether to print tables, diffs, YAML, JSON, warnings, or
status lines.

## Data & Control Flow

1. `main(args)` calls `buildCli().parse(args)` and returns the accumulated `exitCode`. Parse errors
or uncaught exceptions are converted to exit code `1`.
2. `buildCli()` registers root options such as `--debug` and `--config`, a default help action, and
commands for initialization, generation, rendering, stack lifecycle, health checks, reload,
secrets, environment files, planning, and shell completions.
3. Command actions parse Cliffy options, including boolean flags, required string options,
positional arguments, and comma-separated lists such as `--stacks`, `--override`, and `--paths`.
4. Most commands resolve configuration with `resolveConfig({ profile, cwd })`, derive `repoRoot`,
then pass normalized inputs to config, compose, render, docker, env, or secrets functions.
5. Compose workflows commonly discover stack names, generate stack YAML in memory, parse generated
YAML, render variables, optionally write files, and then deploy, reload, compare, or report.
6. Docker-backed commands create a `RealProcessRunner` and delegate Docker CLI interaction to docker
wrapper functions. Dry-run commands use a dry-run runner or skip mutation and print intended
actions.
7. Result objects drive final control flow. Errors and drift set `ExitCode.DriftOrValidation`, user
configuration errors set `ExitCode.UserConfigError`, missing external tooling sets
`ExitCode.MissingDependency`, and uncaught command exceptions set `ExitCode.UnexpectedError`.

## Integration Points

- `config`: `initConfig()` creates `.stackctl` files. `resolveConfig()` loads active configuration
for profile-aware commands. `ExitCode` supplies shared numeric exit semantics.
- `compose`: `generateStacks()` builds canonical stack YAML, `discoverComposeFiles()` discovers
stack names, `syncValidation()` checks generated output against committed stack files,
`reloadStacks()` performs config-first reloads, and `planOperation()` produces deterministic
operation plans.
- `render`: `renderStack()` resolves `${VAR}` placeholders in generated Compose data and reports
unresolved variables and warnings.
- `docker`: Docker wrapper functions perform Swarm and Compose operations through the injected
runner, including stack deploy, removal, service listing, task listing, logs, Compose config
validation, Docker info, and Swarm status checks.
- `env`: Environment commands delegate discovery, creation, diffing, materialization, status
listing, and audit behavior to `discoverEnvExamples()`, `batchCreateEnvs()`, `diffEnvFiles()`,
`materializeEnvFromProfile()`, `getEnvStatusList()`, and `envDoctor()`.
- `secrets`: Secrets commands delegate SOPS and age workflows to `ensureTooling()`,
`checkTooling()`, `findEncryptedEnvFiles()`, `encryptEnvFile()`, `decryptEnvFile()`,
`cleanDecryptedEnvFiles()`, and `deployPipeline()`.
- `process`: `RealProcessRunner` is the runtime adapter for external process execution and dry-run
behavior.
- Deno and standard library APIs provide filesystem writes, temporary files, directory creation,
YAML parsing and serialization, path operations, prompts, and completion command support.
39 changes: 39 additions & 0 deletions src/codemap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# stackctl Codemap

## Responsibility

`stackctl` is a Deno 2.x command line application for repository aware Docker Swarm stack control.
`src/main.ts` is the executable entry point. It delegates all command processing to `src/cli/mod.ts`
and exits with the numeric status returned by that module. `src/version.ts` provides the canonical
CLI version string for runtime use and release automation.

## Design Patterns

- Thin bootstrapper, `src/main.ts` contains process startup only and keeps command behavior inside
the CLI module.
- Explicit entry point guard, `import.meta.main` prevents process execution when the module is
imported.
- Centralized version constant, `VERSION` avoids duplicated version literals in source.
- Capability constrained runtime, the shebang declares Deno permissions for file IO, environment
reads, host inspection, and selected external tools.

## Data & Control Flow

1. Deno starts `src/main.ts` with the permissions declared in the shebang or compiled binary
metadata.
2. `src/main.ts` imports `main` from `src/cli/mod.ts`.
3. When executed directly, `Deno.args` is passed to `main`.
4. The CLI module resolves commands, performs repository and Docker Swarm operations, and returns an
exit code.
5. `Deno.exit` terminates the process with that code.
6. Consumers that need the release identifier import `VERSION` from `src/version.ts`.

## Integration Points

- Runtime, Deno 2.x with JSR imports configured in `deno.json`.
- Package entry, `deno.json` exports `./src/main.ts` as `@anitrend/stackctl`.
- CLI implementation, `src/cli/mod.ts` receives argument vectors and owns command dispatch.
- External tools, Deno run permissions allow `git`, `docker`, `docker-compose`, `sops`, `age`,
`age-keygen`, `shred`, and `rm`.
- Release metadata, `deno.json` version and `src/version.ts` `VERSION` must stay in sync during
version updates.
Loading