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
6 changes: 3 additions & 3 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
- run: cargo test --workspace --all-features
- run: cargo test -p amber-compiler --all-features docker_smoke_ocap_blocks_unbound_callers -- --ignored --test-threads=1
- run: cargo test -p amber-compiler --all-features docker_smoke_config_forwarding_runtime_validation -- --ignored --test-threads=1
- run: cargo test -p amber-compose-helper --all-features helper_image_executes_run_plan_in_scratch -- --ignored --test-threads=1
- run: cargo test -p amber-helper --all-features helper_image_executes_run_plan_in_scratch -- --ignored --test-threads=1

docker-build:
name: Build Docker Images (${{ matrix.image }} ${{ matrix.arch }})
Expand Down Expand Up @@ -78,13 +78,13 @@ jobs:
arch: arm64
- image: amber-helper
context: .
file: docker/amber-compose-helper/Dockerfile
file: docker/amber-helper/Dockerfile
cache_scope: amber-helper-pr-${{ github.event.pull_request.number }}
runner: ubuntu-latest
arch: amd64
- image: amber-helper
context: .
file: docker/amber-compose-helper/Dockerfile
file: docker/amber-helper/Dockerfile
cache_scope: amber-helper-pr-${{ github.event.pull_request.number }}
runner: ubuntu-24.04-arm
arch: arm64
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ jobs:
arch: arm64
- image: amber-helper
context: .
file: docker/amber-compose-helper/Dockerfile
file: docker/amber-helper/Dockerfile
runner: ubuntu-latest
arch: amd64
- image: amber-helper
context: .
file: docker/amber-compose-helper/Dockerfile
file: docker/amber-helper/Dockerfile
runner: ubuntu-24.04-arm
arch: arm64
steps:
Expand Down
40 changes: 33 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ members = [
"cli",
"config",
"compiler",
"compose-helper",
"helper",
"json5",
"manifest",
"node",
Expand All @@ -27,6 +27,7 @@ jsonschema = { version = "0.38.1", default-features = false }
miette = "7.4.0"
serde = "1.0.217"
serde_json = "1.0.148"
serde_yaml = "0.9.34"
tempfile = "3.24.0"
thiserror = "2.0.9"
tokio = "1.48.0"
Expand Down
10 changes: 5 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,23 @@ COPY Cargo.toml Cargo.lock ./
COPY cli/Cargo.toml cli/
COPY config/Cargo.toml config/
COPY compiler/Cargo.toml compiler/
COPY compose-helper/Cargo.toml compose-helper/
COPY helper/Cargo.toml helper/
COPY json5/Cargo.toml json5/
COPY manifest/Cargo.toml manifest/
COPY resolver/Cargo.toml resolver/
COPY scenario/Cargo.toml scenario/
COPY template/Cargo.toml template/
COPY node/Cargo.toml node/

RUN mkdir -p cli/src config/src compiler/src compose-helper/src json5/src manifest/src resolver/src scenario/src template/src node/src && \
touch cli/src/main.rs config/src/lib.rs compiler/src/lib.rs compose-helper/src/main.rs json5/src/lib.rs manifest/src/lib.rs resolver/src/lib.rs scenario/src/lib.rs template/src/lib.rs node/src/main.rs
RUN mkdir -p cli/src config/src compiler/src helper/src json5/src manifest/src resolver/src scenario/src template/src node/src && \
touch cli/src/main.rs config/src/lib.rs compiler/src/lib.rs helper/src/main.rs json5/src/lib.rs manifest/src/lib.rs resolver/src/lib.rs scenario/src/lib.rs template/src/lib.rs node/src/main.rs
RUN cargo fetch --locked
RUN rm -rf cli/src config/src compiler/src compose-helper/src json5/src manifest/src resolver/src scenario/src template/src node/src
RUN rm -rf cli/src config/src compiler/src helper/src json5/src manifest/src resolver/src scenario/src template/src node/src

COPY cli ./cli
COPY config ./config
COPY compiler ./compiler
COPY compose-helper ./compose-helper
COPY helper ./helper
COPY json5 ./json5
COPY manifest ./manifest
COPY resolver ./resolver
Expand Down
2 changes: 1 addition & 1 deletion cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Command-line front-end for the compiler. It resolves a root manifest, runs compi
## Responsibilities
- Wire `amber-compiler` and `amber-resolver` for compile/check flows.
- Render diagnostics via `miette`, including treating selected warnings as errors.
- Write compile outputs only when requested; `amber compile` requires at least one output flag (`--output`, `--dot`, `--docker-compose`/`--compose`, or `--bundle`).
- Write compile outputs only when requested; `amber compile` requires at least one output flag (`--output`, `--dot`, `--docker-compose`/`--compose`, `--kubernetes`, or `--bundle`).
- Detect bundle inputs and emit bundle directories via `--bundle`.
- Surface the manifest README via `amber docs manifest`.

Expand Down
95 changes: 80 additions & 15 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ use amber_compiler::{
CompileOptions, CompileOutput, Compiler, ResolverRegistry,
bundle::{BundleBuilder, BundleLoader},
reporter::{
Reporter as _, docker_compose::DockerComposeReporter, dot::DotReporter,
Reporter as _,
docker_compose::DockerComposeReporter,
dot::DotReporter,
kubernetes::{KubernetesReporter, KubernetesReporterConfig},
scenario_ir::ScenarioIrReporter,
},
};
Expand Down Expand Up @@ -69,6 +72,14 @@ struct CompileArgs {
#[arg(long = "bundle", value_name = "DIR")]
bundle: Option<PathBuf>,

/// Write Kubernetes manifests to this directory.
#[arg(long = "kubernetes", visible_alias = "k8s", value_name = "DIR")]
kubernetes: Option<PathBuf>,

/// Disable generation of NetworkPolicy enforcement check resources.
#[arg(long = "disable-networkpolicy-check", requires = "kubernetes")]
disable_networkpolicy_check: bool,
Comment on lines +80 to +81
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. So far I haven't had a good answer for how to add reporter-specific args. Thanks for making a movement on that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks claude


/// Root manifest or bundle to compile (URL or local path).
#[arg(value_name = "MANIFEST")]
manifest: String,
Expand Down Expand Up @@ -179,6 +190,16 @@ async fn compile(args: CompileArgs) -> Result<()> {
}
}

if let Some(kubernetes_dest) = outputs.kubernetes {
let reporter = KubernetesReporter {
config: KubernetesReporterConfig {
disable_networkpolicy_check: args.disable_networkpolicy_check,
},
};
let artifact = reporter.emit(&output).map_err(miette::Report::new)?;
write_kubernetes_output(&kubernetes_dest, &artifact)?;
}

if let Some(bundle_root) = resolve_bundle_root(&args)? {
let tree = bundle_tree.expect("bundle requested");
prepare_bundle_dir(&bundle_root)?;
Expand Down Expand Up @@ -448,20 +469,22 @@ struct OutputPaths {
primary: Option<PathBuf>,
dot: Option<ArtifactOutput>,
docker_compose: Option<ArtifactOutput>,
kubernetes: Option<PathBuf>,
}

fn ensure_outputs_requested(args: &CompileArgs) -> Result<()> {
if args.output.is_some()
|| args.dot.is_some()
|| args.docker_compose.is_some()
|| args.bundle.is_some()
|| args.kubernetes.is_some()
{
return Ok(());
}

Err(miette::miette!(
help = "Request at least one output with `--output`, `--dot`, `--docker-compose`, or \
`--bundle`.",
help = "Request at least one output with `--output`, `--dot`, `--docker-compose`, \
`--kubernetes`, or `--bundle`.",
"no outputs requested for `amber compile`"
))
}
Expand All @@ -470,6 +493,7 @@ fn resolve_output_paths(args: &CompileArgs) -> Result<OutputPaths> {
let primary = args.output.clone();
let dot = resolve_optional_output(&args.dot);
let docker_compose = resolve_optional_output(&args.docker_compose);
let kubernetes = args.kubernetes.clone();

if let (Some(primary_path), Some(ArtifactOutput::File(dot_path))) =
(primary.as_ref(), dot.as_ref())
Expand Down Expand Up @@ -505,6 +529,7 @@ fn resolve_output_paths(args: &CompileArgs) -> Result<OutputPaths> {
primary,
dot,
docker_compose,
kubernetes,
})
}

Expand All @@ -524,18 +549,10 @@ fn resolve_bundle_root(args: &CompileArgs) -> Result<Option<PathBuf>> {

fn prepare_bundle_dir(path: &Path) -> Result<()> {
if path.exists() {
if path.is_dir() {
std::fs::remove_dir_all(path)
.into_diagnostic()
.wrap_err_with(|| {
format!("failed to remove bundle directory `{}`", path.display())
})?;
} else {
return Err(miette::miette!(
"bundle output path `{}` is not a directory",
path.display()
));
}
return Err(miette::miette!(
"bundle output directory `{}` already exists; please delete it first",
path.display()
));
}

std::fs::create_dir_all(path)
Expand Down Expand Up @@ -565,3 +582,51 @@ fn write_artifact(path: &Path, contents: &[u8]) -> Result<()> {
.into_diagnostic()
.wrap_err_with(|| format!("failed to write `{}`", path.display()))
}

fn write_kubernetes_output(
root: &Path,
artifact: &amber_compiler::reporter::kubernetes::KubernetesArtifact,
) -> Result<()> {
// Clean and recreate the output directory.
if root.exists() {
if root.is_dir() {
std::fs::remove_dir_all(root)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if want

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

like it's not too bad in theory, but the .env files kind of invite you to edit them so

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, maybe let's make people rm -rf <whatever> && amber --k8s <whatever>. not that bad compared to overwriting a .env, which can be really annoying

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok ya

.into_diagnostic()
.wrap_err_with(|| {
format!(
"failed to remove kubernetes output directory `{}`",
root.display()
)
})?;
} else {
return Err(miette::miette!(
"kubernetes output path `{}` is not a directory",
root.display()
));
}
}

std::fs::create_dir_all(root)
.into_diagnostic()
.wrap_err_with(|| {
format!(
"failed to create kubernetes output directory `{}`",
root.display()
)
})?;

// Write each file.
for (rel_path, content) in &artifact.files {
let full_path = root.join(rel_path);
if let Some(parent) = full_path.parent() {
std::fs::create_dir_all(parent)
.into_diagnostic()
.wrap_err_with(|| format!("failed to create directory `{}`", parent.display()))?;
}
std::fs::write(&full_path, content)
.into_diagnostic()
.wrap_err_with(|| format!("failed to write `{}`", full_path.display()))?;
}

Ok(())
}
2 changes: 1 addition & 1 deletion cli/tests/ui/compile_no_output.stderr
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
Error: × no outputs requested for `amber compile`
help: Request at least one output with `--output`, `--dot`, `--docker-
compose`, or `--bundle`.
compose`, `--kubernetes`, or `--bundle`.
1 change: 1 addition & 0 deletions compiler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ jsonschema = { workspace = true }
miette = { workspace = true, features = ["derive"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
serde_yaml = { workspace = true }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

controversial opinion: yaml is a superset of json. write out json instead of bringing in yaml

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok on json

thiserror = { workspace = true }
tokio = { workspace = true, features = ["fs", "rt", "sync"] }
url = { workspace = true, features = ["serde"] }
Expand Down
2 changes: 1 addition & 1 deletion compiler/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ Compiles a root component manifest into a linked `Scenario` plus provenance and
- `frontend`: async resolver with caching, cycle detection, and environment handling.
- `linker`: schema validation, binding resolution, and export verification.
- `passes`: graph rewrites that must preserve scenario invariants.
- `reporter`: transforms `CompileOutput` into artifacts (e.g., scenario IR JSON, DOT, Docker Compose YAML).
- `reporter`: transforms `CompileOutput` into artifacts (e.g., scenario IR JSON, DOT, Docker Compose YAML, Kubernetes YAML).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and then leave it described as kubernetes YAML to mess with people

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nah on labeling it as yaml 😆

- `bundle`: bundle index parsing, manifest packing, and bundle-only resolver wiring.
7 changes: 2 additions & 5 deletions compiler/src/reporter/docker_compose/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use crate::{
const MESH_NETWORK_NAME: &str = "amber_mesh";

const SIDECAR_IMAGE: &str = "ghcr.io/rdi-foundation/amber-sidecar:main";
const HELPER_IMAGE: &str = "ghcr.io/rdi-foundation/amber-compose-helper:v1";
const HELPER_IMAGE: &str = "ghcr.io/rdi-foundation/amber-helper:v1";
const HELPER_VOLUME_NAME: &str = "amber-helper-bin";
const HELPER_INIT_SERVICE: &str = "amber-init";
const HELPER_BIN_DIR: &str = "/amber/bin";
Expand Down Expand Up @@ -607,10 +607,7 @@ fn render_docker_compose_inner(output: &CompileOutput) -> DcResult<String> {
let bindings = binding_values_by_component.get(id).unwrap();

// Root-only composed config template (if available). Root component uses runtime root config.
let template_opt: Option<&rc::ConfigNode> = match resolved_templates.get(id) {
Some(rc::RootConfigTemplate::Node(node)) => Some(node),
_ => None,
};
let template_opt = resolved_templates.get(id).and_then(|t| t.node());

// Build template spec with slots resolved and config either resolved (static) or preserved.
let mut entrypoint_ts: Vec<TemplateString> = Vec::new();
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/reporter/docker_compose/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ fn build_helper_image() -> String {
let root = workspace_root();
build_docker_image(
HELPER_IMAGE,
&root.join("docker/amber-compose-helper/Dockerfile"),
&root.join("docker/amber-helper/Dockerfile"),
&root,
)
}
Expand Down
Loading
Loading