A dotnet tool that makes files deterministic. Rewrites PDFs and System.IO.Packaging containers (nupkg, docx, xlsx, pptx) so the same source always produces byte-identical output. Helpful for testing, build reproducibility, security verification, and ensuring output integrity across different build environments.
It is a command line front end over two libraries, and does no normalizing of its own:
- DeterministicPdf for PDFs.
- DeterministicIoPackaging for System.IO.Packaging containers.
See Milestones for release notes.
dotnet tool install -g Determinize
determinize <path> [options]
path is a file, or a directory containing files. It is rewritten in place unless --target is used.
-t|--targetWrite results here instead of modifying the input in place. An output file path when the input is a file, otherwise a directory mirroring the input tree.-p|--patternSearch patterns applied when the input is a directory. Repeat the option for multiple patterns. Defaults to every extension listed below.-r|--recursiveRecurse into subdirectories when the input is a directory.--checkReport which files are not already deterministic without writing anything. Exits with code 1 if any are found.--continue-on-errorKeep processing the remaining files after a failure, then exit with code 1.-q|--quietSuppress per file and summary output. Errors are still written.-v|--verboseList what changed in each file, indented under it.
A file that is already deterministic is left untouched, so an in place run does not disturb its timestamp.
--verbose lists what was changed in each file, which for --check is the answer to why a file is not reproducible:
determinize ./artifacts -r -v
normalized: artifacts/report.pdf
/CreationDate, /ModDate, /ID x2, xmp:CreateDate, xmp:ModifyDate
xmp:MetadataDate, xmpMM:DocumentID, XMP packet whitespace
normalized: artifacts/tool.nupkg
reordered, removed .signature.p7s, patched [Content_Types].xml
patched _rels/.rels, patched icon.png
2 files processed, 2 normalized.
PDFs name the field that was neutralized; packages name the entry that was removed or patched. Both come from the underlying libraries rather than being inferred here.
Only real differences are listed. Both libraries apply normalizations to every input alike — entry timestamps, compression, XML formatting — and report none of them, because they are the same for every file and so never the reason one file differs from another. A file whose only difference is one of those is reported as changed with that noted in place of a detail list.
Which library handles a file is decided by its extension, so one run covers a mixed directory:
| Extension | Handled by |
|---|---|
.pdf |
DeterministicPdf |
.nupkg .snupkg .vsix |
DeterministicIoPackaging |
.docx .docm .dotx |
DeterministicIoPackaging |
.xlsx .xlsm .xltx |
DeterministicIoPackaging |
.pptx .pptm .potx |
DeterministicIoPackaging |
A file named directly on the command line can carry any extension. An unrecognised one is identified
by its signature instead, so determinize document.bin works if the content is a PDF or a zip.
Plain .zip files are handled, but are deliberately left out of the default patterns so that a
recursive run does not rewrite every archive it finds. Reach them with -p "*.zip".
Determinize one file in place:
determinize document.pdf
Determinize a tree into a separate output directory:
determinize ./input -r --target ./output
Fail a build when any artifact is not deterministic:
determinize ./artifacts -r --check
Only the packages in a directory, leaving everything else alone:
determinize ./artifacts -p "*.nupkg" -p "*.snupkg"