Repository navigation
Releases
v0.5.1
Changed
- The release SBOM lists the dependencies of the
xlsx,parquetandreleaseextras, at the versions the release was tested with. The 0.5.0 SBOM listed none, because the core has no dependencies. - The release docs point to citesync 0.5.0, whose
checkcompares a release folder's RO-Crate and Croissant files withCITATION.cff.
Fixed
releasecopieskeywords,publisherandcontributorfromcodemeta.jsoninto the RO-Crate and Croissant files; before, it dropped them. A publisher given as text, or as an object without@type, is written as an Organization, which both validators require. In the RO-Crate, a person listed twice is one entity.validateandreleasecheck thatdatapackage.jsonhas thenameset insources.toml, so a name added after the last build no longer ships the old one.validate()andrelease()takename=. A script that gates onvalidateexits 1 until the nextbuild.- A
pyproject.tomlthat is not UTF-8, or that cannot be read, now gives the one-linepyproject.toml: ...; fix it, or set name in sources.tomlthat a TOML error already gave, beforebuildwrites any output. release --checkrefuses--codemetaeven when it names./codemeta.json.- The reference says to put
nameabove the first[[source]], where TOML would otherwise make it that source's title.
The SBOM lists the dependency versions this release was tested with, not what an install will resolve.
v0.5.0
Upgrade note for scripts that call the CLI:
- Exit code 3 now means a stage could not run. It was 1.
- Errors on stderr read
error: ...andhelp: ...instead ofSTAGE: .... validateandverifyprint their problems on stdout.--jsonreports have aschema_versionand error objects.- Options go after the stage name.
revisionsprintsno revisionswhen no source changed. It used to print nothing.- In Python,
main()also returns 3 and 130. 0.4.0 returned only 0 or 1.
See options, output and exit codes.
Upgrade note for Python callers and manifests:
read_output()now yields rows instead of returning a list. Wrap it inlist()where the
code indexes or counts them.sources.tomlrefuses top-level keys other thanoutput_format,release_formatsand
[[source]].- A dataset with a recorded file needs 0.5.0 to build.
Details are under Changed.
Added
- A top-level
nameinsources.tomlnames the data package indatapackage.json. Without it,builduses the[project]name inpyproject.toml, and only then the folder's name, so another clone, worktree or CI checkout writes the same file.load_package_namereads it, andbuildtakesname=. release --codemeta PATHreads the dataset's CodeMeta file in place of./codemeta.json. Itslicenseis the dataset's licence, so a project whose data is not under the code's licence keeps a separate file.entry()takesargv, asmain()does, so a project's tests can run its command without patchingsys.argv.- A custom reader may return any mappings as rows, with numbers or None as cells: the
Parsertype now says so. record, andrecord()in the library, take in a file that another tool such as pooch, DVC or your own script downloaded. It copies the file intodata/raw/, checks thesha256pin, and writes the same log line and fetch record asfetch, with arecorded_attime and optional requestparameters, stored as text in name order. The download time is the one the other tool reports, or empty.datapackage.jsonand the HTML report show these outputs as recorded, not fetched, andstatusshows the record time when there is no download time. A recorded URL other than the source's loses its query, and a parameter named like a credential (api_key,apiKey,authKey,sessionid,sig) is refused, since both are published with the dataset. Recording the same bytes again leaves the fetch record as logged; from another URL or download time it adds a log line, andvalidatereports adatapackage.jsonbuilt before such a record. For a source witharchive = true,recordkeeps the snapshot a fetch logged for the same bytes and asks for no new one. A dataset with a recorded file needs this version to build: 0.4.0 refuses its fetch records.sourceledger STAGE --helplists only that stage's options.--dry-runonfetch,recordandbuildsays what they would do and writes nothing.fetch,recordandverifyaccept--json. On a terminal,fetchshows which source it is fetching.- Parquet outputs through
sourceledger[parquet]and the project-wideoutput_format = "parquet"setting. Tables use zstd compression and an explicit string schema, with the same provenance columns and validation as CSV.buildandvalidateacceptoutput_format=in the library, andread_outputreads either format.output_formatinside a[[source]]table, where a line appended tosources.tomllands, is refused, as isrelease_formats.
Writing and validation use batches of at most 10,000 rows through PyArrow, including full-column decoding to detect corruption. load_output_format()reads the project'soutput_format, for library callers to pass tobuildandvalidate.validatein the wrong format names the setting instead of asking you to delete the outputs.entry()runs the command for a project with its own reader, assourceledgerdoes: point the console script at a function that calls it with yourparseandprog(recipe). After Ctrl-C it ends by SIGINT, so a shell loop around it stops.release OUT_DIR, andrelease()in the library, write the folder you publish from a build that validates:data/with the outputs of the sources whoseredistributeistrue, adatapackage.jsonwithout the others, and aSHA256SUMSin the format ofsha256sum -b. Withrelease_formats = ["ro-crate", "croissant"]insources.toml, it also writesro-crate-metadata.json(RO-Crate 1.3) andcroissant.json(Croissant 1.1), with the dataset's name, authors, licence, version and date fromcodemeta.json; a format not asked for is reported as skipped.load_release_formats()reads the key, for library callers to pass torelease.release --check DIRchecks a folder: every file againstSHA256SUMS, and each metadata file with its official validator, rocrate-validator or mlcroissant, from the newsourceledger[release]extra.check_release()is the same check in the library. The report names each check that ran and its version, and a refused release lists its findings underproblemswith exit code 1, as--checkdoes. Without the extra, a check that is needed exits 3 with the install line; a validator that stops without findings, such as offline, or runs past 10 minutes also exits 3. Above 100 files, the Zenodo limit, it warns.ReleaseError,RestrictedSourceErrorandValidatorFailedareValueErrors, andValidatorMissingandValidatorCouldNotRunOSErrors, so code that catches the base classes still works.- A recipe follows a publisher's revision across two PUDL releases:
revisionsflags the new file andvintagenames the release and DOI, thencompare.pydiffs the rows and carries the change into a direct operational CO2 estimate.
Changed
- Breaking:
sources.tomlrefuses two ids that differ only in case, such asPudlandpudl, since on Windows and macOS they name the same file. It also refuses a Windows device name as an id, such asCON,NULorCOM1, in any case. Rename the source, ascon_data. - Breaking:
sources.tomlrefuses aurlwith a non-ASCII character outside its host. Percent-encode it as UTF-8, asd%C3%A4.csvfordä.csv. A non-ASCII host such asbücher.exampleis still accepted. Before,fetchfailed with only'ascii' codec can't encode character. - Breaking: options go after the stage name.
sourceledger --manifest x statusnow exits 2, where 0.4.0 took it. A stage that takes no operand refuses one:sourceledger status extraandbuild fooexit 2 instead of ignoring it. Option prefixes are no longer accepted.validate --jused to run as--json, so write the full option name. - Breaking:
sources.tomlaccepts onlyoutput_format,release_formatsand[[source]]at the top level. A misspelt key such as[[sources]]is refused, with the intended key when one is close. Before, it read as a manifest with no sources, sobuildwrote an emptydatapackage.jsonover a good one. A project that kept its own table there, for its reader's settings, moves it into its own file or into each[[source]], whose other keys stay allowed.output_formatandrelease_formatsare refused inside a[[source]]. - Breaking:
read_output()is a generator that yields one row at a time, so reading a table of 1.5 million rows no longer holds them all in one list. It reads CSV row by row and Parquet in batches of at most 10,000 rows. Wrap the call inlist()where the code indexes the rows, takes their length, or reads them twice. Every error, a missing file included, now raises on the firstnext(). An unfinished iterator keeps the file open, which on Windows blocks replacing the output. - Windows is supported and tested in CI. 0.4.0 said it was not.
build,validateandparse_csvtake 26%, 38% and 25% less time than in 0.4.0 on a 200,000-row CSV table (best of 21 runs on one machine).load_manifesttakes about 0.6 ms longer for 200 sources, for its new checks.- The code behind the public names moved from
sourceledger.ledgerintomanifest,ledger,fetcher,builderandreportmodules. Import fromsourceledgeritself: the submodules may change in any release, as the stability policy says. - The CLI's stages, options, exit codes and
--jsonreports are part of the public API in the stability policy. - The HTML report puts the integrity result in a box at the top, lists the outputs with links under one heading, and names hashes and times in plain words ("SHA-256 of the downloaded file", "Served from"). It follows the reader's dark mode, prints without the in-page links, wraps long ids, and shows a recorded file's request parameters. A script that reads the report's HTML needs updating.
- The CLI help ends with a first run, the exit codes and a link to the docs, and
--versionprints the installed version, after the project's name when a project callsmain(prog=...). Ctrl-C ends a stage with an error, the command to finish it and exit code 130 instead of a traceback, and the command ends by SIGINT so a shell loop around it stops (on Windows it exits 130); under--jsonthe report still prints, with the sources fetched so far.initsays what to do next, and a count of one reads "1 problem" or "1 row". - An unexpected error, such as a project's parser raising
KeyError, prints oneerror:line with its type and exits 3 instead of a traceback. A TOML syntax error names the manifest and the line to fix. A missingrecord,revisionsorverifypath says what to pass. A `...
v0.4.0
Move manually added files out of data/processed/ before running validate.
Added
reportcreates a standalone HTML provenance report that recipients can read offline. It links outputs to their sources and lists integrity problems without including raw data.- An assurance case explains the trust assumptions, safeguards and limits behind integrity and provenance checks.
status,validateandrevisionsaccept--jsonfor scripts and CI. See JSON reports.- Per-source
timeout_ssets the fetch budget, including retries and waits. The default
remains 300 seconds; see download limits and retries. format = "file"keeps files unchanged and validates their bytes, provenance and
reuse metadata. Build describes them as non-tabular resources indatapackage.json;
see How build works.
Changed
-
The documentation website uses a navy and lime reading layout, responsive navigation and copyable code. A connected provenance diagram and revision comparison use an example checked against the pipeline. The full tutorial has its own page.
-
Documentation separates the quick start from the manifest and command reference, custom readers and release verification.
-
Breaking:
validatereports unclaimed non-CSV files indata/processed/, alongside
unclaimed CSV files. See validation.
Fixed
-
JSON status reports render non-text status values as text instead of crashing on TOML dates and times.
-
File validation checks each output hash once while preserving the checks of its provenance and package metadata.
-
The HTML provenance report flags missing, duplicate or no-longer-implemented outputs instead of reporting successful checks for an incomplete package.
-
The citation helper removes old Zenodo version, date and DOI overrides so each release can receive its own archive metadata. See release verification.
-
A malformed resource name in
datapackage.jsonproduces validation problems instead of an uncaught error. -
Command options can appear between the stage and its path on Python 3.12, including
revisions --json OLD_LOG. See the command reference.
Security
- Fetch follows redirects without reading their unused response bodies, avoiding an
unbounded read. See Use sourceledger safely.
v0.3.1
Added
- The README shows how to rebuild a release from its tag and get the same wheel and sdist
as on PyPI, byte for byte.
Changed
initwrites a#:schemaline that names the schema at your installed version's tag,
not the one on main. An editor then checks the manifest against the rules your version
applies, not against keys a later release adds or refuses. Update the line when you
upgrade. The schema's$id, and SchemaStore's entry, stay on main.- Each GitHub release now carries the wheel, the sdist and their signed provenance
bundle (sourceledger-<version>.sigstore.json) as assets. The maintainer pushes a tag,
and the release workflow creates the release.
0.3.0
Upgrade steps
- Rename
releasetovintageinsources.toml. The old key is now refused, with a
message naming the new one (see Removed below). - A source with
headersneeds an httpsurlnow (see Security below). Switch an
http://URL tohttps://, which most APIs serve. - Run
buildagain for JSON sources: numbers are now written as they appear in the file,
so such an output can change (see Changed below).
Added
--manifest PATHon every command, andmanifest=onmain, read asources.toml
other than the one in the current directory, such as one a project ships inside its
package. The data still goes todata/in the current directory.- A weekly CI job runs the Save Page Now contract test against the live archive, so the
stand-in the other tests use is checked against the real service. Run it locally with
uv run pytest -m live. - Two docs pages: How sourceledger works, on the stages and the
files each one writes, and Use sourceledger safely. The README
shows how to verify a release withgh attestation verifyorpypi-attestations.
Changed
- sourceledger is for Linux and macOS (POSIX), and its package metadata now says so. On
Windows it runs, butfetchtakes no lock against a second run. - Reading a table with thousands of columns, or a
sources.tomlwith thousands of sources,
is faster: the check for repeated names no longer grows with the square of their number. - JSON sources keep numbers as written:
1.10stays1.10and1e5stays1e5, at the
top level and inside nested values. Before, they were read as numbers and written back
as1.1and100000.0. An output built from such a file changes on the nextbuild. buildreads a CSV source and writes its output one row at a time, with the default
parser, so memory use no longer grows with the size of the file. For a 161 MiB source it
peaked at 2.5 GiB before, and it is about a quarter faster. The output and
datapackage.json are the same, byte for byte. JSON, Excel, gzipped CSV and a custom
parseare still read whole.validatereads each output one row at a time, and checks the raw file's hash without
reading it into memory, so memory use no longer grows with the size of the data. For a
161 MiB source it peaked at 2.5 GiB before.buildon the command line counts the rows
it prints the same way.fetchchecks a raw file's hash before a conditional request without reading the whole
file into memory, and reads the fetch log once per run. Before, each source with
archive = truethat came back with the same bytes read the whole log again.
Removed
- Breaking: the old key
releaseis no longer read asvintage. A manifest that
still uses it fails to load and names the new key, rather than being kept as an unknown
key and leaving the vintage "not stated".
Fixed
- Bytes in the fetch log that are not UTF-8 no longer stop fetch, status and revisions
with a bare decoding error. They read as U+FFFD, so a line they break is reported by
its number, as any other damaged line is. archive = truenow waits up to 120 s for Save Page Now, up from 15 s. The archive
answers only once the snapshot is taken, 49 s for a GitHub page in one test, so a fetch
whose snapshot took longer than 15 s recorded an emptyarchive_urlalthough the
archive had the page.- A fetch log whose last line is a whole record without its final newline, as an editor
or a merge can leave it, keeps that record. Before, the next fetch took it for a line
cut off by an interrupted fetch and deleted it from the history. Appending now reads
only the end of the log, not all of it. - A source's last record in the fetch log, or its
.fetch.json, with a missing field or a
field of the wrong type (a SHA-256 that is not 64 hex digits, a size that is not a whole
number), is now reported as not a fetch record. Before,fetch,build,validateandrevisions
could stop with a traceback, andfetchskipped the sources after it.fetchnow
downloads such a source again, andvalidatelists it as a problem. revisionsfails when the current log does not exist, as it already did for the
earlier copy. Run from the wrong directory, it used to report no revisions.fetchandbuildcalled from Python with asha256pin in upper case, bypassing
load_manifest, now treat it the same way: both compare it as written.- A cut-off or damaged gzip, a file that is not an Excel workbook, and JSON nested too
deeply to read now failbuildandvalidatewith one line that names the source,
not a traceback. - An Excel sheet with a value right of its header is refused, as a CSV row with more
fields than its header is. Before, it became a column with an empty name, and two such
values gave a misleading "appear more than once" error. Empty header cells at the end
of the first row are no longer read as columns.
Security
- Breaking:
headerson a source whoseurlis not https is a manifest error, since
the API key would cross the network in clear text. - A redirect that changes the scheme, such as https to http on the same host, now drops
theheaderstoo, as a redirect to another host already did. Before, the key went out
in clear text after the downgrade. - A redirect from a public host is now checked against the address the connection
actually reached, not a separate lookup before it. A name that resolved to a public
address for the check and to an internal one for the connection could get past it.
Through a proxy the check is skipped, since the proxy resolves the name. - The 300 s limit on one source's fetch now holds against a server that sends a few
bytes at a time. Each read used to wait for a full 1 MiB, so a slow trickle kept a
fetch going far past the limit. It now stops within one 30 s socket timeout of it. fetchno longer follows a symlink atdata/raw/.lockordata/fetches.jsonl. A
checkout could plant one, and taking the lock emptied the file it pointed at. The lock
file is never truncated now, and a symlinked log fails the fetch.- An Excel workbook is refused when it unzips past
max_bytes, as a gzipped file
already was. A small workbook could otherwise expand to more memory than the machine
has.
v0.2.0
Upgrade steps:
- Rename any source id that uses a non-ASCII letter: ids must be ASCII letters, digits,
_and-(see the Breaking change below). Fetch again afterwards, since the id names
the raw file and the output.
Added
- Python 3.15 is supported and tested in CI.
- JSON (an array of objects), Excel (the
xlsxextra) and gzipped CSV and JSON sources,
read by the new default readerparse_sourcefrom a source'sformator its URL's
extension. A gzipped file is refused when it expands pastmax_bytes. headersinsources.tomlfor API keys: each value names an environment variable
("env:NAME"), literal values are refused, the record keeps the header names only, and a
redirect to another host drops them.buildwritesdata/processed/datapackage.json, a Frictionless Data Package (v2) for
the outputs: columns, size, SHA-256, source URL, licence and fetch record per output.
It has no timestamp of its own, so the same records build the same bytes.validate
reports a package that no longer matches the outputs.- A JSON Schema for
sources.toml, shipped in the package, for editors to check a manifest
as it is typed. A test keeps it in step with whatload_manifestaccepts. sourceledger fetchmarks each lineneworunchanged, against the fetch log.sourceledger initwrites a startersources.tomlwith the schema line and a commented
example source. It never overwrites an existing one.sourceledger statuslists every source with its status, vintage, last fetch, checksum,
licence and whether it may be republished, from the manifest and the log, offline.sourceledger attributionprints the implemented sources' reuse terms as a Markdown
table, for a dataset's README or release notes.sourceledger verify DIRchecks a downloaded dataset against itsdatapackage.json:
every file it lists must exist insideDIR, symlinks included, with the size and
SHA-256 it gives.--only IDforfetch,buildandvalidate, and anonlyargument for the same
functions, to work on some sources only. An unknown id is an error.status,attributionandverifyare also functions in the public API.archive = trueinsources.tomlasks the Internet Archive's Save Page Now to snapshot
a source's URL after a fetch that brings new bytes. The snapshot's address is kept in the
fetch record and the log asarchive_url, and kept when the same bytes are downloaded
again. Each connect and read times out after 15 s,
and a failure is a warning, never a failed fetch. Records written before this field
still load.
Changed
- Breaking: source ids must be ASCII (letters, digits,
_,-). They name files,
which must mean the same file on every system. Before, any Unicode letter passed.
Fixed
- A line in
data/fetches.jsonlthat is JSON but not a fetch record is reported by line
number instead of crashingfetchandrevisions. - A second
fetchin the same project while one is running exits at once instead of
interleaving writes with it (Linux and macOS). - Downloads stream to disk in 1 MiB chunks, so a 200 MiB source no longer needs 200 MiB
of memory, plus copies, to fetch.
Security
- CI gives the read-only GitHub token to the zizmor hook only, not to every hook.
- Each release file on PyPI has a signed GitHub build-provenance attestation:
gh attestation verify <file> --repo simonvanlierde/sourceledgershows where it was built. - CI installs a set prek version, 0.5.5, instead of the newest one on each run.
v0.1.0
chore(release): 0.1.0 (#4) * chore(release): 0.1.0 * chore(release): version and date in the citation files