Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/build_documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ jobs:
steps:
- uses: actions/checkout@v7
with:
ref: ${{ matrix.ref }}
ref: >-
${{ (github.event_name == 'pull_request' && matrix.ref == github.event.pull_request.base.ref)
&& github.event.pull_request.head.sha
|| matrix.ref }}

# - uses: actions/cache@v6
# with:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ Package.resolved
**/*.docc/header.html
**/*.docc/footer.html
scripts/__pycache__/
scripts/.coverage
20 changes: 19 additions & 1 deletion scripts/build_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
from pathlib import Path

from inject_canonical_link import canonicalize_archive as canonicalize_link_archive
from strip_availability import strip_archive
from strip_availability import strip_archive, strip_linux_availability
from strip_language_toggle import strip_archive as strip_language_toggle_archive
from suppress_eyebrows import suppress_archive as suppress_eyebrow_archive
from curate_navigator import (
Expand Down Expand Up @@ -218,6 +218,7 @@ def validate_sources(config):
disallowed_for_archive = (
"targets", "docc_catalog", "path", "repo", "ref",
"preflight", "add_docc_plugin", "extra_flags", "env",
"strip_linux_availability",
)
for field in disallowed_for_archive:
if field in entry:
Expand Down Expand Up @@ -247,6 +248,14 @@ def validate_sources(config):
"'archive' (only allowed on archive sources)"
)

if "strip_linux_availability" in entry and not isinstance(
entry["strip_linux_availability"], bool
):
errors.append(
f"{label} 'strip_linux_availability' must be a boolean "
f"(got {type(entry['strip_linux_availability']).__name__})"
)

if entry.get("add_docc_plugin") and entry_type != "git":
errors.append(f"{label} has 'add_docc_plugin' but is not type 'git'")

Expand Down Expand Up @@ -766,6 +775,15 @@ def build_source(source, root_dir, workspace, common_dir, temp_archive_dir, docc
source, source_dir, common_dir, temp_archive_dir, docc_cmd, env
)

if source.get("strip_linux_availability"):
for archive in archives:
print(f"Stripping Linux availability from {archive}...")
scanned, modified, removed = strip_linux_availability(archive)
print(
f" scanned {scanned} files; modified {modified}; "
f"removed {removed} Linux platform entries"
)

configured_ref = source["ref"] if source_type == "git" else None
actual_ref, commit_sha = _collect_git_metadata(source_dir, configured_ref)

Expand Down
3 changes: 2 additions & 1 deletion scripts/sources.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@
"repo": "https://github.com/swiftlang/swift-testing.git",
"ref": "main",
"targets": ["Testing"],
"add_docc_plugin": true
"add_docc_plugin": true,
"strip_linux_availability": true
},
{
"id": "docc-documentation",
Expand Down
67 changes: 56 additions & 11 deletions scripts/strip_availability.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,13 @@
"""
strip-availability.py — Remove platform availability data from a DocC archive.

Walks every JSON file under <archive>/data/ and deletes any "platforms" key it
finds. In a Swift.doccarchive these only appear in two places:
Walks every JSON file under <archive>/data/ and deletes or modifies any "platforms"
key it finds. In a Swift.doccarchive these only appear in two places:

- metadata.platforms (the iOS/macOS/... badge table
on each symbol page)
- metadata.platforms
(the iOS/macOS/... badge table on each symbol page)
- primaryContentSections[*].declarations[*].platforms
(per-declaration variant tag,
e.g. ["macOS"])
(per-declaration variant tag, e.g. ["macOS"])

Both are populated by DocC at convert-time from the bundle's
CDAppleDefaultAvailability Info.plist key plus any compiler-provided
Expand All @@ -38,6 +37,7 @@
import tempfile

TARGET_KEY = "platforms"
LINUX_PLATFORM_NAME = "Linux"


def strip(node):
Expand All @@ -55,11 +55,45 @@ def strip(node):
return removed


def process_file(path):
def _is_linux_entry(entry):
if isinstance(entry, str):
return entry == LINUX_PLATFORM_NAME
if isinstance(entry, dict):
return entry.get("name") == LINUX_PLATFORM_NAME
return False


def strip_linux(node):
"""Recursively remove only the Linux entry from every TARGET_KEY list.

Unlike strip(), this leaves "platforms" and its other entries (Swift,
Xcode) intact -- it only removes the Linux entry DocC synthesizes from
the build host's target triple.
"""
removed = 0
if isinstance(node, dict):
if TARGET_KEY in node and isinstance(node[TARGET_KEY], list):
before = node[TARGET_KEY]
after = [entry for entry in before if not _is_linux_entry(entry)]
removed += len(before) - len(after)
if len(after) != len(before):
if after:
node[TARGET_KEY] = after
else:
del node[TARGET_KEY]
for v in node.values():
removed += strip_linux(v)
elif isinstance(node, list):
for v in node:
removed += strip_linux(v)
return removed


def process_file(path, strip_fn=strip):
with open(path, "rb") as f:
data = json.load(f)

removed = strip(data)
removed = strip_fn(data)
if removed == 0:
return 0

Expand Down Expand Up @@ -97,8 +131,10 @@ def main():
)


def strip_archive(archive_path):
"""Strip every 'platforms' key from JSON files under <archive>/data/.
def strip_archive(archive_path, strip_fn=strip):
"""Strip 'platforms' data from JSON files under <archive>/data/.

By default deletes every 'platforms' key outright (strip_fn=strip).

Returns (files_scanned, files_modified, keys_removed).
Raises ValueError if archive_path doesn't look like a .doccarchive
Expand All @@ -123,7 +159,7 @@ def strip_archive(archive_path):
path = os.path.join(root, name)
files_scanned += 1
try:
removed = process_file(path)
removed = process_file(path, strip_fn)
except json.JSONDecodeError as e:
sys.stderr.write(f"skip (invalid JSON): {path}: {e}\n")
continue
Expand All @@ -134,5 +170,14 @@ def strip_archive(archive_path):
return files_scanned, files_modified, keys_removed


def strip_linux_availability(archive_path):
"""Remove only the Linux entry from every 'platforms' list in an archive.

Thin wrapper around strip_archive() using strip_linux() -- see its
docstring for why this is narrower than the default full wipe.
"""
return strip_archive(archive_path, strip_fn=strip_linux)


if __name__ == "__main__":
main()
Loading