Skip to content
Open
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
57 changes: 57 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,61 @@ See **[docs/adopting.md](docs/adopting.md)** for existing repos, configuration r
| `devShell` | `famedly-*` CLI (see above) |
| `rules` | `.cursor/rules/…`, `CLAUDE.md` |
| Workflows | `ci`, `general-checks`, `dart-ci` (multi-package), `rust-ci`, `docker` (multi-arch/simple), `review-app`, `github-pages`, `hookd-deploy`, `release`, `publish-crate`, `publish-pub`, `docker-backend`, `docker-bake`, `ansible-ci`, `ai-review`, `fast-forward`, `add-to-project`, `update-engineering-standards` |
| `platform` | local k3d + Tilt dev environment (wraps `famedly-platform`) |
| `projects` | monorepo: per-folder lint/hooks/dependabot |

---

## Platform dev environment

Enable a local Famedly platform (k3d + Tilt) that builds your service image on every code change while all other services run from pre-built registry images.

```nix
perSystem = { ... }: {
famedly.standards.platform = {
enable = true;
image = {
name = "famedly-operator"; # must match the Helm subchart name
chart = ./helm/famedly-operator;
};
};
};
```

Then start with `nix run .#famedly-platform-up` and tear down with `nix run .#famedly-platform-down`.

### All platform options

| Option | Default | Description |
|--------|---------|-------------|
| `image.name` | `null` | Helm subchart name — Tilt builds this image locally |
| `image.src` | `"."` | Docker build context (relative to repo root) |
| `image.build.docker` | `null` | Dockerfile path (default: `<src>/Dockerfile`) |
| `image.build.nix` | `null` | Nix flake package for container image (e.g. `"my-container"`) |
| `image.chart` | `null` | Local Helm chart dir — overrides the published subchart |
| `image.hotReload` | `[]` | Push file changes into the container without rebuild |
| `image.patch.cargo` | `{}` | Cargo `[patch]` overrides for local Rust dependency development |
| `extraImages` | `[]` | Additional local images (monorepo setups) |
| `chart` | pinned `helm-charts` | Platform umbrella chart source |
| `values` | `{}` | Additional Helm values |
| `ports` | 9310, 8080, 8008, 8282 | k3d port mappings |
| `testCommand` | — | CI mode: run after environment is ready |
| `extraManifests` | `[]` | Extra K8s manifests to apply before the chart |

### Hot-reload (frontends)

```nix
image.hotReload = [
{ from = "build/web"; to = "/usr/share/nginx/html"; }
];
```

### Cargo patch (Rust dependency override)

```nix
image.patch.cargo = {
"https://github.com/famedly/zitadel-rust-client" = "../zitadel-rust-client";
};
```

See [`famedly-platform` README](https://github.com/famedly/famedly-platform) for full platform documentation.
10 changes: 7 additions & 3 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
url = "github:famedly/helm-charts";
flake = false;
};
famedly-platform = {
url = "github:famedly/famedly-platform";
inputs.flake-parts.follows = "flake-parts";
};
};

outputs =
Expand All @@ -42,6 +46,7 @@
standards = ./nix/modules;
workflows = importApply ./nix/modules/workflows args;
preCommitHooks = importApply ./nix/modules/pre-commit-hooks.nix args;
platform = importApply ./nix/modules/platform.nix args;
};
in
{
Expand Down Expand Up @@ -141,10 +146,9 @@
enable = true;
fossHooks.enable = false;
};
e2e = {
platform = {
enable = true;
chart = "${inputs.helm-charts}/e2e-platform";
portForwards = [
ports = [
"9310:9310@loadbalancer"
"8080:8080@loadbalancer"
"8282:8282@loadbalancer"
Expand Down
231 changes: 231 additions & 0 deletions nix/modules/platform.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
# Local platform dev environment module.
#
# Wraps famedly-platform for single-repo development: one `enable` flag
# and a concise `image` description is all a consumer needs.
#
# perSystem = { ... }: {
# famedly.standards.platform = {
# enable = true;
# image = {
# name = "famedly-operator";
# chart = ./helm/famedly-operator;
# };
# };
# };
#
# Then run `famedly-platform-up` to start k3d + Tilt with the local
# image build. All other platform services use pre-built registry images.

{
inputs,
flake-parts-lib,
...
}:
_caller-args:
{
imports = [ inputs.famedly-platform.flakeModules.default ];

options.perSystem = flake-parts-lib.mkPerSystemOption (
{
config,
pkgs,
lib,
...
}:
let
cfg = config.famedly.standards.platform;
img = cfg.image;
hasImage = img.name != null;

localImages =
(lib.optional hasImage (
{
subchart = img.name;
context = img.src;
}
// lib.optionalAttrs (img.build.docker != null) {
dockerfile = img.build.docker;
}
// lib.optionalAttrs (img.build.nix != null) {
nix_target = img.build.nix;
}
// lib.optionalAttrs (img.hotReload != [ ]) {
live_update = map (r: {
sync = {
local = r.from;
container = r.to;
};
}) img.hotReload;
}
// lib.optionalAttrs (img.patch.cargo != { }) {
cargo_patch = img.patch.cargo;
}
))
++ cfg.extraImages;

localChartOverrides = lib.optional (hasImage && img.chart != null) img.chart;
in
{
options.famedly.standards.platform = {
enable = lib.mkEnableOption "Local platform dev environment (k3d + Tilt)";

image = {
name = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = ''
Service name (must match the Helm subchart name in the
platform umbrella chart). When set, Tilt builds this
repo's container image locally on every source change.
'';
example = "famedly-operator";
};

src = lib.mkOption {
type = lib.types.str;
default = ".";
description = "Docker build context directory, relative to the repo root.";
};

build = {
docker = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = ''
Path to the Dockerfile (relative to the repo root).
When null, defaults to <src>/Dockerfile.
Mutually exclusive with build.nix.
'';
};

nix = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = ''
Nix flake package that produces a container image
(e.g. "famedly-control-service-container").
When set, Tilt uses `nix build` instead of `docker build`.
Mutually exclusive with build.docker.
'';
example = "famedly-control-service-container";
};
};

chart = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
description = ''
Local Helm chart directory. Overrides the published
subchart in the umbrella chart — use this to test chart
changes alongside code changes.
'';
example = "./helm/famedly-operator";
};

hotReload = lib.mkOption {
type = lib.types.listOf (
lib.types.submodule {
options = {
from = lib.mkOption {
type = lib.types.str;
description = "Local path (relative to src) to watch and push.";
};
to = lib.mkOption {
type = lib.types.str;
description = "Container path to receive the files.";
};
};
}
);
default = [ ];
description = ''
Hot-reload rules: push changed files directly into the
running container without rebuilding the image. Useful
for frontend builds (HTML/JS/CSS → nginx).
'';
example = [
{
from = "build/web";
to = "/usr/share/nginx/html";
}
];
};

patch = {
cargo = lib.mkOption {
type = lib.types.attrs;
default = { };
description = ''
Cargo [patch] overrides — maps git URLs to local paths
for developing against a local checkout of a Rust dependency.
'';
example = {
"https://github.com/famedly/zitadel-rust-client" = "../zitadel-rust-client";
};
};
};
};

extraImages = lib.mkOption {
type = lib.types.listOf lib.types.attrs;
default = [ ];
description = ''
Additional local images for monorepo setups.
Uses the same schema as famedly.platform.localImages.
'';
};

testCommand = lib.mkOption {
type = lib.types.str;
default = "echo 'No test command configured'";
description = "Command executed by `famedly-platform` (CI mode) after the environment is ready.";
};

chart = lib.mkOption {
type = lib.types.either lib.types.str (lib.types.attrsOf lib.types.str);
default = "${inputs.helm-charts}/e2e-platform";
description = ''
Platform umbrella chart source. Defaults to the pinned
helm-charts input from engineering-standards.
'';
};

values = lib.mkOption {
type = lib.types.attrs;
default = { };
description = "Additional Helm values for the platform chart.";
};

ports = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [
"9310:9310@loadbalancer"
"8080:8080@loadbalancer"
"8008:8008@loadbalancer"
"8282:8282@loadbalancer"
];
description = "k3d cluster port mappings (format: host:container@loadbalancer).";
};

extraManifests = lib.mkOption {
type = lib.types.listOf lib.types.path;
default = [ ];
description = "Additional Kubernetes manifests to apply before the platform chart.";
};
};

config = lib.mkIf cfg.enable {
famedly.platform = {
enable = true;
chart = cfg.chart;
values = cfg.values;
ports = cfg.ports;
testCommand = cfg.testCommand;
extraManifests = cfg.extraManifests;
localImages = localImages;
localChartOverrides = localChartOverrides;
};
};
}
);
}
Loading