feat(bootstrap): Bun-workspace skeleton + typed config + vendored modules — T02
- Bun workspaces (packages/* + bootstrap); Pulumi nodejs runtime under
packagemanager: bun (no npm fallback needed).
- bootstrap/config.ts: typed FoundationConfig per CONTRACT_001; loadConfig()
fails closed, aggregating all missing+malformed keys in one error. Reads flat
dotted keys; image digests excluded (they live in VERSIONS, D5).
- bootstrap/Pulumi.foundation.yaml: non-secret placeholders only (RFC-5737 vm.host,
.invalid offsite); no encryptionsalt/secrets committed (D2). pulumi preview = 0
resources under the passphrase provider via gitignored file:// state backend.
- Stage-1 vendoring: packages/pulumi-{docker,vault} as @olsitec/* (source-only,
logic unchanged). vault's 5 type-only imports from modules/olsitec re-homed
verbatim into pulumi-vault/olsitec-types.ts to keep the egg self-contained.
Realizes PLAN-002 §10 T02; ADR-005 / 000_TOPOLOGY.md §5 Stage-1.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
edc708b826
commit
57c4eadea7
26 changed files with 2758 additions and 0 deletions
139
documentation/agents/task_002_pulumi_skeleton/003_handoff.md
Normal file
139
documentation/agents/task_002_pulumi_skeleton/003_handoff.md
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
# T02 — Pulumi project skeleton — Handoff
|
||||
|
||||
**Task:** PLAN-002 §10 T02 · **Mode:** BUILD · **Date:** 2026-06-30
|
||||
**Author role:** implementation agent · **Status:** complete, validated by execution (see below).
|
||||
|
||||
> The lead reviews and commits. Nothing was `git add`/`git commit`ed by this task.
|
||||
|
||||
---
|
||||
|
||||
## 1. Files created / touched
|
||||
|
||||
### Workspace root
|
||||
- `package.json` — Bun workspace root (`@olsitec/foundation`, private, `workspaces: ["packages/*","bootstrap"]`).
|
||||
- Removed placeholders `bootstrap/.gitkeep`, `packages/.gitkeep` (replaced by real content).
|
||||
- `bun.lock` — generated by `bun install` (links the workspace; no secrets — verified).
|
||||
|
||||
### Vendored module `packages/pulumi-docker/` (`@olsitec/pulumi-docker`)
|
||||
- `index.ts`, `tsconfig.json`, `.editorconfig`, `.gitignore` — copied **verbatim** from
|
||||
`/Users/andiolsi/work/olsicloud4/pulumi/modules/docker/`.
|
||||
- `package.json` — renamed `docker` → `@olsitec/pulumi-docker`; added `version` `0.0.0`,
|
||||
`main`/`types` (`index.ts`), and an explicit `@pulumi/pulumi` dependency (see §4).
|
||||
- `VENDORED.md` — source path, copy date, Stage-1 note, Stage-2 deferral.
|
||||
|
||||
### Vendored module `packages/pulumi-vault/` (`@olsitec/pulumi-vault`)
|
||||
- `index.ts`, `policy.ts`, `tsconfig.json`, `.editorconfig`, `.gitignore` — copied **verbatim**
|
||||
from `/Users/andiolsi/work/olsicloud4/pulumi/modules/vault/`.
|
||||
- `olsitec-types.ts` — **NEW**: the 5 type-only declarations the upstream `index.ts` imported
|
||||
from `../../modules/olsitec`, copied verbatim (see §3). The single import line in `index.ts`
|
||||
was re-pointed `../../modules/olsitec` → `./olsitec-types`. No runtime logic changed.
|
||||
- `tsconfig.json` — `files` extended with `policy.ts` + `olsitec-types.ts` (standalone typecheck).
|
||||
- `package.json` — renamed `vault` → `@olsitec/pulumi-vault`; `version`/`main`/`types`; added
|
||||
explicit `@pulumi/pulumi` dependency (§4).
|
||||
- `VENDORED.md` — source path, copy date, Stage-1 note, the type-only re-home, and a flag that the
|
||||
inherited Layer-1 credential surface (`VaultProject`/minio/garage/…) is to be trimmed in a later
|
||||
Layer-0 refactor (NOT in Stage-1, which preserves source).
|
||||
|
||||
### `bootstrap/` (the egg, single Pulumi project)
|
||||
- `Pulumi.yaml` — `name: foundation`, `runtime: nodejs` + `options.packagemanager: bun`.
|
||||
- `package.json` — depends on `@olsitec/pulumi-docker`/`@olsitec/pulumi-vault` (`workspace:*`) + `@pulumi/pulumi`.
|
||||
- `tsconfig.json` — Olsitec-standard compiler options; `files: [config.ts, index.ts]`.
|
||||
- `config.ts` — `FoundationConfig` interface (CONTRACT_001 §1.1) + `loadConfig()` (fails closed) + `sshPrivateKeyPath()` (ENV).
|
||||
- `index.ts` — no-op scaffold: calls `loadConfig()`, creates NO resources, exports non-secret outputs.
|
||||
- `Pulumi.foundation.yaml` — NON-secret placeholders only; no secrets, no encryptionsalt.
|
||||
|
||||
### Documentation
|
||||
- `documentation/agents/task_002_pulumi_skeleton/000_subtask_outline.md`
|
||||
- `documentation/agents/task_002_pulumi_skeleton/003_handoff.md` (this file)
|
||||
|
||||
---
|
||||
|
||||
## 2. Validated-by-execution vs. authored-only
|
||||
|
||||
**Validated by running it (env: bun 1.3.9, pulumi v3.243.0, node v24.10.0, macOS):**
|
||||
- `bun install` links the workspace; `bun pm ls` shows all three workspace members;
|
||||
`require.resolve("@olsitec/pulumi-docker"|"@olsitec/pulumi-vault")` from `bootstrap` resolves to
|
||||
`packages/*/index.ts`. ✅ (acceptance: workspace resolves the two packages)
|
||||
- `tsc --noEmit` exit 0 for **all three** projects (bootstrap + both vendored packages). ✅
|
||||
- `pulumi preview` under the **passphrase** secrets provider (local `file://bootstrap/state` backend
|
||||
+ throwaway `PULUMI_CONFIG_PASSPHRASE` in ENV): exit 0, loads config, **0 real resources** (only the
|
||||
no-op Stack), prints the scaffold outputs. ✅ (acceptance: preview runs on empty/stub stack, passphrase provider)
|
||||
- **Fail-closed demonstrated:** removing `foundation:vm.host` + `foundation:features.backup` made
|
||||
`pulumi preview` exit 1 with one error listing both missing keys; restoring made it pass again. ✅
|
||||
(acceptance: config rejects missing required keys)
|
||||
- **No secrets committed:** `bootstrap/state/` is gitignored (`git check-ignore` confirms); the
|
||||
committed `Pulumi.foundation.yaml` has **no** real `encryptionsalt:`/`secure: v1:` value line
|
||||
(only explanatory comment prose mentions the words); `bun.lock` has no secret leakage. ✅
|
||||
|
||||
**Authored but NOT executed (out of scope — no VM, no `pulumi up` ever):**
|
||||
- Actual Docker-over-SSH provisioning, Vault init/unseal, any container — all LATER tasks (T03+).
|
||||
- The vendored modules' runtime behaviour was not exercised (they are libraries; only typechecked +
|
||||
consumed by the typechecked bootstrap). Their logic is byte-identical to the upstream source apart
|
||||
from the documented type-only re-home in pulumi-vault.
|
||||
|
||||
---
|
||||
|
||||
## 3. CONTRACT_001 ambiguities / decisions
|
||||
|
||||
- **Config key shape (resolved by execution).** CONTRACT_001 §1.1 shows a nested interface but §1.2
|
||||
gives flat dotted example keys (`foundation:hosts.forge`, `foundation:features.forgejo`). Pulumi
|
||||
stores and exposes these **flat** — `getObject("hosts")` does NOT reassemble `hosts.forge`+`hosts.vault`
|
||||
into an object. `loadConfig()` therefore reads each leaf by its full dotted key with the typed
|
||||
accessor for its kind (`get`/`getNumber`/`getBoolean`, and `getObject` for the two arrays), then
|
||||
assembles the nested `FoundationConfig`. This is the idiomatic match for the §1.2 YAML and is robust
|
||||
to Pulumi quoting scalars (`"2222"`, `"true"`). No contract change needed.
|
||||
- **No further ambiguity blocked typing.** Every §1.1 field maps 1:1 to a placeholder key.
|
||||
- **Secrets (§1.3) intentionally not required** by `loadConfig()` — they are seeded by later tasks
|
||||
(T05 Vault capture etc.); requiring them now would block the T02 acceptance (empty/stub stack).
|
||||
|
||||
## 4. Bun-vs-npm decision + rationale
|
||||
|
||||
**Decision: Bun** (Olsitec footgun 16.3 prefers Bun; 000_TOPOLOGY.md §3 specifies Bun workspaces).
|
||||
- Pulumi's nodejs runtime supports `options.packagemanager: bun`; `pulumi preview` ran cleanly under
|
||||
it (the program executes via Pulumi's bundled ts-node against bun-installed deps). **No fallback to
|
||||
npm was needed.**
|
||||
- **One vendoring fix forced by Bun's isolated store:** the upstream modules never declared
|
||||
`@pulumi/pulumi` directly (they relied on it being hoisted into their own `node_modules`). Bun's
|
||||
`.bun/` store layout does not hoist it where standalone `tsc` on a package can find it, so both
|
||||
vendored `package.json`s now list `@pulumi/pulumi` (`^3.138.0`, matching olsitec-core) as an
|
||||
explicit dependency. This is a **packaging-metadata** correction (the import was always real); **no
|
||||
module logic changed**. It also makes the packages correct for Stage-2 publishing. (This is the
|
||||
escalation-worthy "vendoring reveals a dependency" item — judged minor and fixed in place, not a
|
||||
design change.)
|
||||
|
||||
## 5. Recommended follow-ups
|
||||
|
||||
1. **Strip `encryptionsalt` before each commit of `Pulumi.foundation.yaml`.** Every `pulumi
|
||||
preview/up` under the passphrase provider re-appends an `encryptionsalt:` line and re-quotes
|
||||
scalars. The committed file has been left clean; a header comment documents the strip step. Once
|
||||
the first real secret is added (T05), the encryptionsalt becomes load-bearing and SHOULD then be
|
||||
committed alongside the `secure: v1:` values — revisit this guidance at that point.
|
||||
2. **`packages/pulumi-vault` Layer-0 trim (later task).** `VaultBootstrap`/`VaultProject` still carry
|
||||
the Layer-1 minio/garage/cockroach/mongo credential surface. The egg only needs
|
||||
`VaultInitialization` (init/unseal capture) + a minimal `VaultBootstrap`. Trim per 000_TOPOLOGY.md
|
||||
§5.1 "refactor for Layer 0" — out of scope for Stage-1 vendoring.
|
||||
3. **Run `pin-digests`** to replace `PIN_DIGEST` in `VERSIONS` before any real `pulumi up` (T01/preflight concern).
|
||||
4. **Stage-2 publish** (`@olsitec/pulumi-*` to the foundation Forgejo npm registry) once it exists —
|
||||
semantic-release-monorepo + Conventional Commits (000_TOPOLOGY.md §5, memory: olsitec-charts-conventional-commits).
|
||||
5. **Lint config** not vendored — the upstream modules had no per-package eslint file (only an
|
||||
`.editorconfig`, which was copied); olsitec-core's `eslint.config.mjs` is project-level. Add a
|
||||
workspace-level lint setup in a later tooling task if desired.
|
||||
|
||||
## 6. How to reproduce the validation
|
||||
|
||||
```sh
|
||||
cd ~/work/olsitec-foundation/foundation
|
||||
bun install
|
||||
bunx tsc --noEmit -p bootstrap/tsconfig.json
|
||||
bunx tsc --noEmit -p packages/pulumi-docker/tsconfig.json
|
||||
bunx tsc --noEmit -p packages/pulumi-vault/tsconfig.json
|
||||
|
||||
cd bootstrap
|
||||
export PULUMI_HOME="$(pwd)/state/.pulumi-home"
|
||||
export PULUMI_CONFIG_PASSPHRASE='<throwaway-dev-only>' # never commit
|
||||
pulumi login "file://$(pwd)/state"
|
||||
pulumi stack init foundation --secrets-provider=passphrase # first time only
|
||||
pulumi preview --stack foundation
|
||||
# NOTE: preview rewrites Pulumi.foundation.yaml — `git checkout` it or strip the
|
||||
# appended `encryptionsalt:` line before committing. state/ is gitignored.
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue