Summary
Every HTML build that places Runestone Services in its own output (the default; not portable
HTML, nor resources from the CDN) ends up with 225 macOS AppleDouble
files in _static/, one beside each real file:
_static/._139.6404c46752b3e634.js
_static/._139.6404c46752b3e634.js.gz
_static/._139.6404c46752b3e634.js.map
...
Each is 163 bytes of Finder metadata (file reports "AppleDouble encoded Macintosh
file"), used by nothing. They are harmless in a browser, but they double the file count
of _static/ (450 files where 225 are wanted), and they travel with every site that is
uploaded or zipped from the output.
Cause
They are in the archive itself. dist-8.3.2.tgz from https://runestone.academy/cdn/runestone/
has 450 entries, 225 of them ._*, all owned by bmiller/staff; dist-8.2.13.tgz is the
same. This is what macOS tar does by default: it writes each file's extended
attributes as a ._ companion. The archive also carries LIBARCHIVE.xattr.com.apple.provenance
headers, which GNU tar reports as "Ignoring unknown extended header keyword" on every
entry; Python's tarfile passes over those silently.
_place_runestone_services (pretext/lib/pretext.py, lines 3958–3959 at 20d10545)
unpacks everything:
services_file = tarfile.open(services_build_path)
services_file.extractall(build_dir)
Suggested fix
Either end would do, and both would be best.
In the script, skip the AppleDouble members when extracting:
services_file = tarfile.open(services_build_path)
members = [m for m in services_file.getmembers()
if not os.path.basename(m.name).startswith("._")]
services_file.extractall(build_dir, members=members)
Tried on dist-8.3.2.tgz: 225 files come out, none of them ._*, and they are exactly
the files a build places now, less the AppleDouble ones.
In the Runestone packaging, make the archive without the macOS metadata, for example
COPYFILE_DISABLE=1 tar --no-xattrs -czf dist-<version>.tgz ..., which also removes the
provenance headers. That fix belongs to the Runestone side.
Reproduce
Any HTML build with the default static files, at 20d10545:
pretext -c doc -f html -p publication.ptx -d out main.ptx
ls -A out/_static | grep -c '^\._' # 225
A related observation
extractall() without a filter argument raises a DeprecationWarning on Python 3.12
and later ("Python 3.14 will, by default, filter extracted tar archives..."); it is not
seen because the warning comes from a library module. Passing filter="data" where
tarfile has it (3.12, and the security releases 3.10.12 and 3.11.4) would settle that
at the same spot.
Summary
Every HTML build that places Runestone Services in its own output (the default; not portable
HTML, nor resources from the CDN) ends up with 225 macOS AppleDouble
files in
_static/, one beside each real file:Each is 163 bytes of Finder metadata (
filereports "AppleDouble encoded Macintoshfile"), used by nothing. They are harmless in a browser, but they double the file count
of
_static/(450 files where 225 are wanted), and they travel with every site that isuploaded or zipped from the output.
Cause
They are in the archive itself.
dist-8.3.2.tgzfromhttps://runestone.academy/cdn/runestone/has 450 entries, 225 of them
._*, all owned bybmiller/staff;dist-8.2.13.tgzis thesame. This is what macOS
tardoes by default: it writes each file's extendedattributes as a
._companion. The archive also carriesLIBARCHIVE.xattr.com.apple.provenanceheaders, which GNU
tarreports as "Ignoring unknown extended header keyword" on everyentry; Python's
tarfilepasses over those silently._place_runestone_services(pretext/lib/pretext.py, lines 3958–3959 at20d10545)unpacks everything:
Suggested fix
Either end would do, and both would be best.
In the script, skip the AppleDouble members when extracting:
Tried on
dist-8.3.2.tgz: 225 files come out, none of them._*, and they are exactlythe files a build places now, less the AppleDouble ones.
In the Runestone packaging, make the archive without the macOS metadata, for example
COPYFILE_DISABLE=1 tar --no-xattrs -czf dist-<version>.tgz ..., which also removes theprovenance headers. That fix belongs to the Runestone side.
Reproduce
Any HTML build with the default static files, at
20d10545:A related observation
extractall()without afilterargument raises aDeprecationWarningon Python 3.12and later ("Python 3.14 will, by default, filter extracted tar archives..."); it is not
seen because the warning comes from a library module. Passing
filter="data"wheretarfilehas it (3.12, and the security releases 3.10.12 and 3.11.4) would settle thatat the same spot.