fdl-cli 0.12.1__tar.gz → 0.13.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/CHANGELOG.md +11 -0
  2. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/PKG-INFO +1 -1
  3. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/docs/spec.md +2 -1
  4. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/pyproject.toml +1 -1
  5. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/src/fdl/repo.py +10 -2
  6. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/tests/test_repo.py +12 -3
  7. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/.github/workflows/docs.yml +0 -0
  8. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/.github/workflows/publish.yml +0 -0
  9. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/.github/workflows/release.yml +0 -0
  10. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/.gitignore +0 -0
  11. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/.python-version +0 -0
  12. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/CLAUDE.md +0 -0
  13. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/CONTRIBUTING.md +0 -0
  14. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/LICENSE +0 -0
  15. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/README.md +0 -0
  16. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/docs/CNAME +0 -0
  17. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/docs/cli.md +0 -0
  18. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/docs/index.md +0 -0
  19. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/src/fdl/__init__.py +0 -0
  20. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/src/fdl/catalog.py +0 -0
  21. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/src/fdl/cli.py +0 -0
  22. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/src/fdl/layout.py +0 -0
  23. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/src/fdl/py.typed +0 -0
  24. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/src/fdl/refs.py +0 -0
  25. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/src/fdl/store.py +0 -0
  26. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/src/fdl/stores.py +0 -0
  27. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/src/fdl/version.py +0 -0
  28. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/tests/test_cli.py +0 -0
  29. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/tests/test_stores.py +0 -0
  30. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/uv.lock +0 -0
  31. {fdl_cli-0.12.1 → fdl_cli-0.13.0}/zensical.toml +0 -0
@@ -1,6 +1,17 @@
1
1
  # CHANGELOG
2
2
 
3
3
 
4
+ ## v0.13.0 (2026-11-01)
5
+
6
+ ### Features
7
+
8
+ - Give each repository an id in fdl.json
9
+ ([`389ec96`](https://github.com/queria-io/fdl/commit/389ec96899d7c33fe0fbcf11d6d958de8b1fcb1a))
10
+
11
+ init writes a UUID that never changes, so a copy of a repository can tell it holds the same one.
12
+ Repositories started without one still read.
13
+
14
+
4
15
  ## v0.12.1 (2026-10-30)
5
16
 
6
17
  ### Bug Fixes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: fdl-cli
3
- Version: 0.12.1
3
+ Version: 0.13.0
4
4
  Summary: Versions of a DuckLake catalog on object storage, kept the way Git keeps commits
5
5
  Project-URL: Homepage, https://fdl.flo8s.com/
6
6
  Project-URL: Documentation, https://fdl.flo8s.com/
@@ -58,6 +58,7 @@ Paths are relative to the root, lower case, separated by `/`. The root is what a
58
58
  ```json
59
59
  {
60
60
  "fdl": "0.1",
61
+ "id": "5f0c8a52-6d3e-4f8e-9b1a-2c7d4e9f0a13",
61
62
  "data_dir": "data/",
62
63
  "catalog_formats": [
63
64
  { "ducklake": "1.1" },
@@ -67,6 +68,7 @@ Paths are relative to the root, lower case, separated by `/`. The root is what a
67
68
  ```
68
69
 
69
70
  - `fdl` is the version of this specification. A reader must not read a major version it does not know
71
+ - `id` is a UUID written when the repository is started and never changed, so a copy of the repository can tell it holds the same one. A service that hosts repositories may use its own ID for the dataset here. Repositories started without one are still read
70
72
  - `data_dir` is where data files are. The default is `data/`. A repository whose files already sit under another name declares it here instead of moving them
71
73
  - `catalog_formats` lists the DuckLake formats published and until when (see "When the DuckLake format moves"). Without it, only `ducklake.duckdb`
72
74
  - Files with other names may sit at the root. Readers ignore them
@@ -276,7 +278,6 @@ A push from a stale working catalog is refused on two checks:
276
278
  - How to make a merge (a version with two parents)
277
279
  - A compatibility test suite for readers and writers
278
280
  - Withdrawal: removing past versions entirely, for example when personal data was published by mistake (`reason: withdrawn`, rewriting Parquet, and the fact that mirrors keep their copies)
279
- - A repository ID: an immutable `id` in `fdl.json`, so a mirror can tell it holds the same dataset
280
281
  - Signatures, for readers of a mirror: anyone can write a `version.json`
281
282
  - How to declare retention defaults in `fdl.json` (versions to keep, days)
282
283
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fdl-cli"
3
- version = "0.12.1"
3
+ version = "0.13.0"
4
4
  description = "Versions of a DuckLake catalog on object storage, kept the way Git keeps commits"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -11,6 +11,7 @@ import hashlib
11
11
  import itertools
12
12
  import json
13
13
  import shutil
14
+ import uuid
14
15
  from dataclasses import dataclass
15
16
  from pathlib import Path
16
17
  from typing import Iterator
@@ -32,6 +33,9 @@ class NotFastForward(RepoError):
32
33
  class Config:
33
34
  fdl: str
34
35
  data_dir: str
36
+ #: Never changes, so a copy of the repository can tell it holds the same one.
37
+ #: None for repositories started before it was written.
38
+ id: str | None = None
35
39
 
36
40
 
37
41
  def init(store: Store, *, data_dir: str = layout.DEFAULT_DATA_DIR) -> Config:
@@ -42,7 +46,7 @@ def init(store: Store, *, data_dir: str = layout.DEFAULT_DATA_DIR) -> Config:
42
46
  """
43
47
  if not data_dir.endswith("/"):
44
48
  raise RepoError(f"data_dir has to end with '/': {data_dir!r}")
45
- record = {"fdl": layout.FDL_VERSION, "data_dir": data_dir}
49
+ record = {"fdl": layout.FDL_VERSION, "id": str(uuid.uuid4()), "data_dir": data_dir}
46
50
  try:
47
51
  store.put(
48
52
  layout.CONFIG,
@@ -67,7 +71,11 @@ def read_config(store: Store) -> Config:
67
71
  major = str(record.get("fdl", "")).split(".")[0]
68
72
  if major != layout.FDL_VERSION.split(".")[0]:
69
73
  raise RepoError(f"written for fdl {record.get('fdl')}, this reads {layout.FDL_VERSION}")
70
- return Config(fdl=record["fdl"], data_dir=record.get("data_dir", layout.DEFAULT_DATA_DIR))
74
+ return Config(
75
+ fdl=record["fdl"],
76
+ data_dir=record.get("data_dir", layout.DEFAULT_DATA_DIR),
77
+ id=record.get("id"),
78
+ )
71
79
 
72
80
 
73
81
  def read_version(store: Store, version_id: str) -> Version:
@@ -7,6 +7,7 @@ version that comes out the other side.
7
7
  from __future__ import annotations
8
8
 
9
9
  import json
10
+ import uuid
10
11
  from pathlib import Path
11
12
 
12
13
  import duckdb
@@ -64,11 +65,19 @@ def read_rows(store: LocalStore, version_id: str) -> list[tuple]:
64
65
 
65
66
 
66
67
  def test_init_writes_the_config_and_head(remote) -> None:
67
- assert json.loads(remote.get(layout.CONFIG)) == {"fdl": "0.1", "data_dir": "data/"}
68
+ config = json.loads(remote.get(layout.CONFIG))
69
+ assert config == {"fdl": "0.1", "id": config["id"], "data_dir": "data/"}
70
+ assert uuid.UUID(config["id"])
68
71
  assert remote.get(layout.HEAD) == b"ref: refs/heads/main\n"
69
72
  assert repo.head(remote) is None
70
- # Twice is harmless.
71
- assert repo.init(remote).data_dir == "data/"
73
+ # Twice is harmless, and the id stays.
74
+ assert repo.init(remote) == repo.Config(fdl="0.1", data_dir="data/", id=config["id"])
75
+
76
+
77
+ def test_a_repository_without_an_id_still_reads(tmp_path: Path) -> None:
78
+ store = LocalStore(tmp_path / "old")
79
+ store.put(layout.CONFIG, b'{"fdl": "0.1", "data_dir": "ducklake.duckdb.files/"}')
80
+ assert repo.read_config(store).id is None
72
81
 
73
82
 
74
83
  def test_commit_push_and_read_back(tmp_path: Path, remote) -> None:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes