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
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ ENV SNOWFLAKE_ADBC_DRIVER_PATH=/usr/local/lib/libadbc_driver_snowflake.so
# persistent-file-log feature in the deployed image; a local/dev invocation
# of this same binary outside the image still gets it by default.
ENV DATAMK_LOG=off
# The cell mounts read-only; eject artifacts go to the scratch emptyDir.
ENV DATAMK_MATERIALIZE_DIR=/tmp/materialize
# Bake every DuckDB extension the engine may INSTALL at first use (~200 MB:
# ducklake, httpfs, json, postgres, sqlite, community bigquery) into the
# image, so a pod needs no registry egress and no exec-capable scratch
Expand Down
2 changes: 2 additions & 0 deletions docs/guides/incremental.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ Other rules:
inherits nothing.
- The composed statements are written to `.cell/materialize/<table>.sql` every
run (audit and portability; overwritten; not a valid `transforms:` target).
`DATAMK_MATERIALIZE_DIR` redirects them; the container image sets it to
`/tmp/materialize` because the cell mounts read-only there.
- One-off backfills and manual corrections: `datamk attach -f cell.yaml -p prod`
and run SQL against the lake directly.

Expand Down
29 changes: 28 additions & 1 deletion src/engine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1250,13 +1250,26 @@ fn execute_materialize(
/// only for `replace` (schema-validated — `append`/`upsert` always carry a
/// non-empty `key:`), which never ran any guard to lose, so its header says
/// that plainly instead of naming guards that don't apply to it.
/// Where the eject artifacts land: `DATAMK_MATERIALIZE_DIR` if set, else
/// `.cell/materialize` under the cell directory. The override exists for
/// the same reason `DATAMK_LOG=off` does (`logging.rs`): a deployed Builder
/// mounts the cell read-only from a ConfigMap, so the default path is
/// unwritable there and the image points this at the scratch emptyDir.
/// Env-only, deliberately not a CLI flag — a laptop run never needs it.
fn materialize_dir(dir: &Path) -> PathBuf {
match std::env::var_os("DATAMK_MATERIALIZE_DIR") {
Some(d) if !d.is_empty() => PathBuf::from(d),
_ => dir.join(".cell").join("materialize"),
}
}

fn write_eject_artifact(
dir: &Path,
table: &str,
key: &[String],
statements: &[String],
) -> Result<PathBuf> {
let materialize_dir = dir.join(".cell").join("materialize");
let materialize_dir = materialize_dir(dir);
std::fs::create_dir_all(&materialize_dir)
.with_context(|| format!("creating {}", materialize_dir.display()))?;
let path = materialize_dir.join(format!("{table}.sql"));
Expand Down Expand Up @@ -6158,6 +6171,20 @@ mod tests {
assert!(artifact.exists());
}

#[test]
fn materialize_dir_honours_the_env_override_and_ignores_an_empty_one() {
// Nothing else in this binary reads `DATAMK_MATERIALIZE_DIR`, so
// mutating it here is safe (same discipline as the DATAMK_LOG test).
let dir = Path::new("/some/cell");
std::env::remove_var("DATAMK_MATERIALIZE_DIR");
assert_eq!(materialize_dir(dir), dir.join(".cell").join("materialize"));
std::env::set_var("DATAMK_MATERIALIZE_DIR", "");
assert_eq!(materialize_dir(dir), dir.join(".cell").join("materialize"));
std::env::set_var("DATAMK_MATERIALIZE_DIR", "/tmp/materialize");
assert_eq!(materialize_dir(dir), PathBuf::from("/tmp/materialize"));
std::env::remove_var("DATAMK_MATERIALIZE_DIR");
}

// --- ADR 0008 §3 (founder-ratified): `materialize: replace` ------------

#[test]
Expand Down
2 changes: 1 addition & 1 deletion test/integrations/kind_e2e/manifests/minio.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ spec:
spec:
containers:
- name: minio
image: minio/minio:latest
image: quay.io/minio/minio:RELEASE.2025-07-23T15-54-02Z
args: ["server", "/data", "--console-address", ":9001"]
env:
- name: MINIO_ROOT_USER
Expand Down
6 changes: 4 additions & 2 deletions test/integrations/kind_e2e/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ spec:
restartPolicy: Never
containers:
- name: mc
image: minio/mc:latest
image: quay.io/minio/mc:RELEASE.2025-07-21T05-28-08Z
command:
- /bin/sh
- -c
Expand Down Expand Up @@ -399,7 +399,9 @@ phase_validate() {
log "validate: GET /openapi.json, discovering the export route"
local openapi route
openapi="$(curl -fsS "$base/openapi.json")" || die "GET /openapi.json failed"
route="$(echo "$openapi" | jq -r '.paths | keys[0]')"
# Export routes carry '@<major>'; the spec also documents /, /context,
# /context/{route}, and /openapi.json, which sort ahead of them.
route="$(echo "$openapi" | jq -r '[.paths | keys[] | select(contains("@"))][0]')"
[ -n "$route" ] && [ "$route" != "null" ] || die "openapi.json has no routes"
log "discovered route: $route"

Expand Down
Loading