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
26
packages/pulumi-docker/.editorconfig
Normal file
26
packages/pulumi-docker/.editorconfig
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# EditorConfig helps developers define and maintain consistent
|
||||
# coding styles between different editors and IDEs
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.{diff,md}]
|
||||
trim_trailing_whitespace = false
|
||||
indent_size = 4
|
||||
|
||||
[Makefile]
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
3
packages/pulumi-docker/.gitignore
vendored
Normal file
3
packages/pulumi-docker/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
/bin/
|
||||
/node_modules/
|
||||
*.stack*.json
|
||||
35
packages/pulumi-docker/VENDORED.md
Normal file
35
packages/pulumi-docker/VENDORED.md
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# VENDORED — `@olsitec/pulumi-docker`
|
||||
|
||||
**Source (absolute path):** `/Users/andiolsi/work/olsicloud4/pulumi/modules/docker/`
|
||||
**Copy date:** 2026-06-30
|
||||
**Stage:** Stage-1 vendoring per [`documentation/000_TOPOLOGY.md` §5](../../documentation/000_TOPOLOGY.md).
|
||||
|
||||
## What this is
|
||||
|
||||
A verbatim copy of the olsicloud4 `modules/docker` Pulumi module (the
|
||||
`DockerDeployments` `@pulumi/docker`-over-SSH wrapper, CONTRACT_003). At day-zero the
|
||||
foundation registry does not yet exist, so `bootstrap/` consumes this module locally
|
||||
through the Bun workspace (`packages/*`) instead of from a registry — this resolves the
|
||||
"registry hosts the modules that build the registry" paradox (000_TOPOLOGY.md §3, ADR-005).
|
||||
|
||||
## What was copied
|
||||
|
||||
`index.ts`, `package.json`, `tsconfig.json`, `.editorconfig`, `.gitignore`.
|
||||
|
||||
**Not copied:** `node_modules/`, `package-lock.json` (lockfiles), `.git/`.
|
||||
|
||||
## Changes made vs. the source
|
||||
|
||||
- `package.json` `name`: `docker` → `@olsitec/pulumi-docker`; added `version` (`0.0.0`,
|
||||
pre-publish placeholder) and `main`/`types` (`index.ts`) so the Bun workspace resolves it.
|
||||
- **No behavioural change.** `index.ts` logic is byte-for-byte the upstream source.
|
||||
|
||||
## Lifecycle (000_TOPOLOGY.md §5)
|
||||
|
||||
- **Stage 1 — VENDOR (this commit):** copied here; consumed locally via Bun workspace.
|
||||
- **Stage 2 — PUBLISH (later task):** once the foundation Forgejo npm registry is live, CI
|
||||
publishes `@olsitec/pulumi-docker@<semver>` (semantic-release-monorepo, Conventional Commits).
|
||||
- **Stage 3 — CONSUME (steady state):** downstream projects switch imports from
|
||||
`olsicloud4/pulumi/modules/docker` to the published package; the old module is frozen then removed.
|
||||
|
||||
Do not refactor the vendored logic here. Behavioural changes belong upstream or in Stage-2+ work.
|
||||
175
packages/pulumi-docker/index.ts
Normal file
175
packages/pulumi-docker/index.ts
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
// index.ts
|
||||
import * as pulumi from "@pulumi/pulumi";
|
||||
import { Output } from "@pulumi/pulumi";
|
||||
import * as docker from "@pulumi/docker";
|
||||
|
||||
export type DockerDeploymentsContainerArgs = {
|
||||
name: string;
|
||||
providerName: string;
|
||||
image: string;
|
||||
ports?: {
|
||||
internal: number;
|
||||
external: number;
|
||||
protocol?: "tcp" | "udp";
|
||||
ip?: string;
|
||||
// hostname?: string;
|
||||
}[];
|
||||
volumes?: {
|
||||
source: string;
|
||||
target: string;
|
||||
readOnly?: boolean;
|
||||
type?: "bind" | "volume" | "tmpfs";
|
||||
}[];
|
||||
networks?: {
|
||||
network: string;
|
||||
aliases?: string[];
|
||||
}[];
|
||||
envs?: (string | Output<string>)[];
|
||||
config: Partial<docker.ContainerArgs>;
|
||||
providerConfig?: Partial<pulumi.CustomResourceOptions>;
|
||||
};
|
||||
interface DockerDeploymentsArgs {
|
||||
provider: Record<
|
||||
string,
|
||||
{
|
||||
host: pulumi.Input<string>;
|
||||
sshPrivateKeyPath?: string;
|
||||
networks?: Record<string, docker.NetworkArgs>;
|
||||
} & Partial<docker.ProviderArgs>
|
||||
>;
|
||||
containers: DockerDeploymentsContainerArgs[];
|
||||
}
|
||||
export class DockerDeployments extends pulumi.ComponentResource {
|
||||
private providers: Record<string, docker.Provider>;
|
||||
public containers: {
|
||||
container: docker.Container;
|
||||
image: Output<docker.GetRegistryImageResult>;
|
||||
volumes?: docker.Volume[];
|
||||
}[];
|
||||
private networks: Record<string, docker.Network>;
|
||||
constructor(
|
||||
name: string,
|
||||
args: DockerDeploymentsArgs,
|
||||
opts?: pulumi.ComponentResourceOptions,
|
||||
) {
|
||||
super("custom:resource:DockerDeployments", name, {}, opts);
|
||||
|
||||
this.providers = Object.entries(args.provider).reduce(
|
||||
(acc, [providerName, providerConfig]) => {
|
||||
acc[providerName] = new docker.Provider(
|
||||
`${name}-${providerName}`,
|
||||
{
|
||||
host: providerConfig.host,
|
||||
sshOpts: providerConfig.sshPrivateKeyPath
|
||||
? [
|
||||
"-o",
|
||||
"StrictHostKeyChecking=no",
|
||||
"-o",
|
||||
"UserKnownHostsFile=/dev/null",
|
||||
"-i",
|
||||
providerConfig.sshPrivateKeyPath,
|
||||
]
|
||||
: undefined,
|
||||
registryAuth: providerConfig.registryAuth,
|
||||
},
|
||||
{
|
||||
parent: this,
|
||||
},
|
||||
);
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, docker.Provider>,
|
||||
);
|
||||
this.networks = {};
|
||||
Object.entries(args.provider).forEach(([providerName, providerConfig]) => {
|
||||
if (providerConfig.networks) {
|
||||
Object.entries(providerConfig.networks).forEach(
|
||||
([name, networkConfig]) => {
|
||||
this.networks[`${providerName}-${name}`] = new docker.Network(
|
||||
`${providerName}-${name}`,
|
||||
{
|
||||
name: name,
|
||||
attachable: true,
|
||||
driver: "bridge",
|
||||
internal: false,
|
||||
...networkConfig,
|
||||
},
|
||||
{
|
||||
parent: this,
|
||||
provider: this.providers[providerName],
|
||||
deleteBeforeReplace: true,
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
this.containers = [];
|
||||
for (const containerConfig of args.containers) {
|
||||
const provider = this.providers[containerConfig.providerName];
|
||||
const containerImage = pulumi.output(
|
||||
docker.getRegistryImage(
|
||||
{
|
||||
name: containerConfig.image,
|
||||
},
|
||||
{
|
||||
parent: this,
|
||||
provider: provider,
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
const container = new docker.Container(
|
||||
`${containerConfig.providerName}-${containerConfig.name}`,
|
||||
{
|
||||
image: containerImage.apply((image) => image.name),
|
||||
name: containerConfig.name,
|
||||
hostname: containerConfig.name,
|
||||
ports: containerConfig.ports?.map((port) => ({
|
||||
internal: port.internal,
|
||||
external: port.external,
|
||||
protocol: port.protocol || "tcp",
|
||||
ip: port.ip,
|
||||
})),
|
||||
mounts: containerConfig.volumes?.map((volume) => ({
|
||||
source: volume.source,
|
||||
target: volume.target,
|
||||
readOnly: volume.readOnly || false,
|
||||
type: volume.type || "volume",
|
||||
})),
|
||||
networksAdvanced: containerConfig.networks?.map((network) => ({
|
||||
name: network.network,
|
||||
aliases: network.aliases || [containerConfig.name],
|
||||
})),
|
||||
envs: containerConfig.envs
|
||||
? [
|
||||
...containerConfig.envs,
|
||||
pulumi.interpolate`IMAGE_ID=${containerImage.apply((image) => image.id)}`,
|
||||
]
|
||||
: [
|
||||
pulumi.interpolate`IMAGE_ID=${containerImage.apply((image) => image.id)}`,
|
||||
],
|
||||
...containerConfig.config,
|
||||
},
|
||||
{
|
||||
parent: this,
|
||||
provider: provider,
|
||||
...containerConfig.providerConfig,
|
||||
ignoreChanges: containerConfig.providerConfig?.ignoreChanges
|
||||
? [...containerConfig.providerConfig.ignoreChanges, "image"]
|
||||
: ["image"],
|
||||
deleteBeforeReplace: true,
|
||||
dependsOn:
|
||||
containerConfig.networks && containerConfig.networks[0].network
|
||||
? [this.networks[containerConfig.networks[0].network]]
|
||||
: [],
|
||||
},
|
||||
);
|
||||
this.containers.push({
|
||||
image: containerImage,
|
||||
container: container,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
18
packages/pulumi-docker/package.json
Normal file
18
packages/pulumi-docker/package.json
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "@olsitec/pulumi-docker",
|
||||
"version": "0.0.0",
|
||||
"main": "index.ts",
|
||||
"types": "index.ts",
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3.1.0",
|
||||
"@pulumi/eslint-plugin": "^0.2.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.13.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"prettier": "^3.3.3",
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@pulumi/docker": "^4.5.8",
|
||||
"@pulumi/pulumi": "^3.138.0"
|
||||
}
|
||||
}
|
||||
18
packages/pulumi-docker/tsconfig.json
Normal file
18
packages/pulumi-docker/tsconfig.json
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"outDir": "bin",
|
||||
"target": "es2020",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"experimentalDecorators": true,
|
||||
"pretty": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitReturns": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
},
|
||||
"files": [
|
||||
"index.ts"
|
||||
]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue