Repository navigation
feat(registry)!: copy a release between registries - #649
Conversation
82a9c32 to
ff71a9d
Compare
✅ CI checks passed
Updated for |
ff71a9d to
d05e135
Compare
TimoStoff
left a comment
There was a problem hiding this comment.
LGTM! Minor nits, but please fix them docstrings!
d05e135 to
a2d446e
Compare
a4ec699 to
b22699a
Compare
b22699a to
2c9667e
Compare
ade7cd5 to
ebe933c
Compare
RealLast
left a comment
There was a problem hiding this comment.
Good direction, thank you! But please let's give it a bit more solid and declarative design; we are building a release system here and can have classes and well-defined interfaces to realize this
RealLast
left a comment
There was a problem hiding this comment.
As discussed, accepting to unblock : ) We can clean up later
ebe933c to
83d5205
Compare
83d5205 to
908925d
Compare
908925d to
1225d48
Compare
There was a problem hiding this comment.
An organization admin can review the limit in your organization's usage and billing settings.
Once the limit resets or is raised, someone with write access to this repository can comment @claude review on this pull request to trigger a review.
copy_release(reference, source, dest) copies a release and each payload source that its manifest lists into another registry. A release needs exactly those releases, never the payload sources of a payload source. With with_payload_sources=False, the copy takes the release alone. Sources come first, and each release is copied once. A published version cannot change: if the destination holds the same file records, nothing happens, or a local destination gets the release again from the source. If it holds other files, TimeNetVersionConflictError stops the copy before anything is written. Every check runs before the first copy, access included: a registry that is not local serves files only for releases whose card and lineage declare access open. Between two registries that are not local, only the missing release is downloaded. find_payload_sources() in timenet.registry.dependencies applies this rule and the access check. It finds each payload source in the registry to copy from, else as a complete copy in the destination. The typed progress events live in timenet.progress. The step events carry the release and the action, so their shape stays the same while the coordinator's step type grows. Part of TNET-157.
…or users copy_release() is now part of the public names of timenet.registry, next to LocalRegistry. Callers import it with `from timenet.registry import copy_release`. Its module is now the private timenet/registry/_copy.py, like _paths.py. The module docstring describes the copy in plain words and no longer lists the internal steps. The docstrings of this change say "non-local" for a registry that is not local, and the TimeNetVersionConflictError text says that a registry "already holds" the release. Part of TNET-157.
…he copy docstrings BaseRegistry.requires_open_access tells whether a registry holds and serves only releases with open access. The access checks use it instead of a test for LocalRegistry. BaseRegistry.download_release() is public. TimeNetMissingParentError takes payload_source as a keyword argument. timenet.progress lists only the events that a copy sends. The docstrings of copy_release() are shorter. Part of TNET-157.
Shorter docstrings for the code that this change adds or changes. Part of TNET-157.
A copy compared only the file records with the copy in the destination. So a rebuild with the same files and another card or lineage counted as the same release, and the destination kept the old card. Now the copy compares the whole manifest without its build environment. TimeNetVersionConflictError names the difference: "files" or "card". A forced download deleted the old copy before it renamed the new one in place. If the rename failed, no copy was left. Both download paths now stage the files with staged_directory(), which keeps the old copy until the new one is in place. download_release() takes a DatasetRef, as copy_release() does. The registries get find_manifest(), which returns None for a release that a registry does not hold, and LocalRegistry.damage(), which names the first missing or resized file. The copy uses both instead of its own checks. Part of TNET-157.
1225d48 to
3e30447
Compare
The Stack
This stack covers refactoring the internal logic of how releases are resolved during read, download & build, in particular also in the context of parent releases / parent datasets. On top of that it refactors the public TimeNet APIs and CLI interfaces around these actions, to streamline the behavior across them.
This PR
Adds
timenet.registry.copy_release(), which copies a release between registries and never changes a published version.Details
Builds, downloads and publishing all copy releases between registries. Other releases with inheritance pin the files of a published version, so a copy must not change it.
timenet.registry.copy_release(reference, source, dest, *, with_payload_sources=True, progress=None)copies a release and the payload sources that its manifest lists into another registry. Withwith_payload_sources=False, it copies the release alone.BaseRegistry.download_release(reference, directory, ...)is public now. It takes the release as aDatasetRef. A failed download keeps the old copy.BaseRegistry.find_manifest()returns the manifest of a release. For a release that the registry does not hold, it returnsNone.LocalRegistry.damage()returns the error for the first missing or resized file of a stored release, orNone.TimeNetVersionConflictErrorreports another build of the same version, with the release, the registries and the difference ("files"or"card") as attributes.StepStartedandStepFinishedlive intimenet.progress.copy_release()is their first sender.Design decisions
copy_release()compares the whole manifest of each release withtimenet.manifest.same_release(), without the build environment. The file records (path, size and checksum) come first, then the card and the rest of the manifest. If the manifests are the same, nothing happens, or a local destination gets the release again from the source. If they differ,TimeNetVersionConflictErrorstops the copy before anything is written, and its message asks for a new version. An overwrite breaks every release that pins the old files.destneeds no more. Sources come first, and each release is copied once and alone.find_payload_sources()intimenet.registry.dependencies, applies this rule and the one access check. The planner and the download plan later in the stack use the same walker. A walk of one level cannot loop, so the copy needs no cycle check.BaseRegistry.requires_open_accessis true by default and false onLocalRegistry. The copy refuses a release that is not open whendest, or thesourcethat it fetches from, requires open access.Breaking changes
BaseRegistry._download_release(dataset_id, version, dest_dir, ...)is nowdownload_release(reference, directory, ...). A registry of another package that overrides it must rename the override.TimeNetMissingParentErrortakes(missing, needed_by, searched, *, payload_source). Before,payload_sourcewas the third positional argument.Stack
Changes later in the stack:
Merge message