Skip to content
This repository was archived by the owner on Jan 27, 2025. It is now read-only.

Synchronize with monorepo #28

Merged
merged 1 commit into from
Oct 2, 2023
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
2 changes: 1 addition & 1 deletion .flake8
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: MIT OR Apache-2.0
# SPDX-FileCopyrightText: Ferrous Systems and AdaCore
# SPDX-FileCopyrightText: The Ferrocene Developers

# Configuration recommended by Black
# https://black.readthedocs.io/en/stable/guides/using_black_with_other_tools.html#flake8
Expand Down
2 changes: 1 addition & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: MIT OR Apache-2.0
# SPDX-FileCopyrightText: Ferrous Systems and AdaCore
# SPDX-FileCopyrightText: The Ferrocene Developers

version: 2
updates:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: MIT OR Apache-2.0
# SPDX-FileCopyrightText: Ferrous Systems and AdaCore
# SPDX-FileCopyrightText: The Ferrocene Developers

---

Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: MIT OR Apache-2.0
# SPDX-FileCopyrightText: Ferrous Systems and AdaCore
# SPDX-FileCopyrightText: The Ferrocene Developers

__pycache__
*.py[co]
154 changes: 0 additions & 154 deletions LICENSES/CC-BY-ND-4.0.txt

This file was deleted.

1 change: 1 addition & 0 deletions LICENSES/LicenseRef-Proprietary.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
All rights reserved.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!-- SPDX-License-Identifier: MIT OR Apache-2.0 -->
<!-- SPDX-FileCopyrightText: Ferrous Systems and AdaCore -->
<!-- SPDX-FileCopyrightText: The Ferrocene Developers -->

# Ferrocene's shared Sphinx resources

Expand Down
173 changes: 173 additions & 0 deletions exts/ferrocene_domain_cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
# SPDX-License-Identifier: MIT OR Apache-2.0
# SPDX-FileCopyrightText: The Ferrocene Developers

# This extension defines the "cli" domain, which contains the "program" and
# "option" directives. Those directives are used to document the CLI interface
# of the binaries we ship.
#
# Sphinx *has* builtin "program" and "option" directives, but unfortunately
# they assume that the name of the command line option is just the one that
# starts with "-" (for example "--crate-name"), but for our use case we want to
# consider "-C opt-level=<level>" as the "-C opt-level" argument.

from docutils import nodes
from sphinx import addnodes
from sphinx.directives import SphinxDirective, ObjectDescription
from sphinx.domains import Domain, ObjType
from sphinx.roles import XRefRole
import re
import sphinx
import string


PROGRAM_STORAGE = "ferrocene_domain_cli:program"


class ProgramDirective(SphinxDirective):
has_content = True
required_arguments = 1
final_argument_whitespace = True

def run(self):
if PROGRAM_STORAGE in self.env.temp_data:
warn("cli:program inside cli:program isn't supported", self.get_location())
return []
self.env.temp_data[PROGRAM_STORAGE] = self.arguments[0]

node = nodes.container()
self.state.nested_parse(self.content, self.content_offset, node)

del self.env.temp_data[PROGRAM_STORAGE]
return [node]


class OptionDirective(ObjectDescription):
has_content = True
required_arguments = 1
option_spec = {}

def handle_signature(self, sig, signode):
signode += addnodes.desc_name("", sig)

def add_target_and_index(self, name_cls, sig, signode):
if PROGRAM_STORAGE not in self.env.temp_data:
warn("cli:option outside cli:program isn't supported", self.get_location())
program = "PLACEHOLDER"
else:
program = self.env.temp_data[PROGRAM_STORAGE]

option = Option(self.env.docname, program, sig)

signode["ids"].append(option.id())

domain = self.env.get_domain("cli")
domain.add_option(option)


ARGUMENT_PLACEHOLDER_RE = re.compile(r"(<[^>]+>|\[[^\]]+\])")
MULTIPLE_UNDERSCORES_RE = re.compile(r"__+")
ALLOWED_CHARS_IN_OPTION_ID = string.ascii_letters + string.digits + "_"


class Option:
def __init__(self, document, program, option):
self.document = document
self.program = program
self.option = option

def id(self):
option = (
ARGUMENT_PLACEHOLDER_RE.sub("", self.option)
.replace("=", "")
.replace("-", "_")
.replace(" ", "_")
.strip("_")
)
option = MULTIPLE_UNDERSCORES_RE.sub("_", option)

# Sanity check to notice when the normalization doesn't work
if any(c for c in option if c not in ALLOWED_CHARS_IN_OPTION_ID):
warn(f"cannot properly normalize option {self.option}")

return f"um_{self.program}_{option}"


class CliDomain(Domain):
name = "cli"
labels = "Command-line interface"
roles = {
"option": XRefRole(),
}
directives = {
"program": ProgramDirective,
"option": OptionDirective,
}
object_types = {
"option": ObjType("CLI option", "option"),
}
initial_data = {"options": {}}
# Bump whenever the format of the data changes!
data_version = 1

def add_option(self, option):
self.data["options"][f"{option.program} {option.option}"] = option

def get_options(self):
return self.data["options"]

def clear_doc(self, docname):
self.data["options"] = {
key: item
for key, item in self.data["options"].items()
if item.document != docname
}

def merge_domaindata(self, docnames, otherdata):
for key, option in otherdata["options"].items():
if option.document in docnames:
self.data["options"][key] = option

def resolve_xref(self, env, fromdocname, builder, type, target, node, contnode):
if type != "option":
raise RuntimeError(f"unsupported xref type {type}")

if target not in self.data["options"]:
return
option = self.data["options"][target]

return sphinx.util.nodes.make_refnode(
builder, fromdocname, option.document, option.id(), contnode
)

def get_objects(self):
for key, option in self.data["options"].items():
yield (
key, # Name
f"{option.program} {option.option}", # Display name
"option", # Type
option.document, # Document name
option.id(), # Anchor
0, # Priority
)


def warn(message, location):
logger = sphinx.util.logging.getLogger(__name__)
logger.warn(message, location=location)


def setup(app):
app.add_domain(CliDomain)

return {
"version": "0",
"parallel_read_safe": True,
"parallel_write_safe": True,
# The version needs to be updated whenever there is a breaking change
# in the data stored in the environment. Bumping the version number
# will ensure Sphinx will do a clean build.
#
# Version history:
# - 0: initial implementation
"env_version": "0",
}
2 changes: 1 addition & 1 deletion exts/ferrocene_intersphinx_support.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: MIT OR Apache-2.0
# SPDX-FileCopyrightText: Ferrous Systems and AdaCore
# SPDX-FileCopyrightText: The Ferrocene Developers

# This extension adds some helpers needed to integrate Ferrocene's build system
# with InterSphinx. More specifically, the extension:
Expand Down
7 changes: 5 additions & 2 deletions exts/ferrocene_qualification/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: MIT OR Apache-2.0
# SPDX-FileCopyrightText: Ferrous Systems and AdaCore
# SPDX-FileCopyrightText: The Ferrocene Developers

from . import substitutions, document_id, domain, signature_page
import string
Expand All @@ -14,7 +14,10 @@ def setup(app):
app.connect("config-inited", validate_config)
app.add_config_value("ferrocene_id", None, "env", [str])
app.add_config_value("ferrocene_substitutions_path", None, "env", [str])
app.add_config_value("ferrocene_signed", False, "env", [str])
app.add_config_value("ferrocene_signature", None, "env", [str])
app.add_config_value("ferrocene_private_signature_files_dir", None, "env", [str])
app.add_config_value("ferrocene_version", None, "env", [str])
app.add_config_value("rust_version", None, "env", [str])

return {
"version": "0",
Expand Down
2 changes: 1 addition & 1 deletion exts/ferrocene_qualification/document_id.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: MIT OR Apache-2.0
# SPDX-FileCopyrightText: Ferrous Systems and AdaCore
# SPDX-FileCopyrightText: The Ferrocene Developers

# This module is responsible for generating the ID of the whole document. This
# ID is supposed to uniquely identify the revision of the document, and it must
Expand Down
2 changes: 1 addition & 1 deletion exts/ferrocene_qualification/domain.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: MIT OR Apache-2.0
# SPDX-FileCopyrightText: Ferrous Systems and AdaCore
# SPDX-FileCopyrightText: The Ferrocene Developers

from docutils import nodes
from sphinx.directives import ObjectDescription
Expand Down
66 changes: 51 additions & 15 deletions exts/ferrocene_qualification/signature_page.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: MIT OR Apache-2.0
# SPDX-FileCopyrightText: Ferrous Systems and AdaCore
# SPDX-FileCopyrightText: The Ferrocene Developers

import datetime
import json
Expand All @@ -13,30 +13,40 @@ class SignatureStatus:
def __init__(self, app):
self.app = app
self.loaded_files = []
self.copiable_files = []
self.copiable_files = {}
self.context = {}
self.private_files = {}

if not app.config.ferrocene_signed:
if app.config.ferrocene_signature is None:
self.state = "not_needed"
return
elif app.config.ferrocene_signature == "missing":
self.state = "unsigned"
return
elif app.config.ferrocene_signature != "present":
raise RuntimeError("invalid value to ferrocene_signature")

try:
self.context["config"] = tomli.loads(self.load_file("config.toml"))
self.load_file("pinned.toml", copy=True)
self.private_files = tomli.loads(self.load_file("signature.toml"))["files"]
self.load_private_file("pinned.toml", copy=True)

self.context["signatures"] = {}
for role in self.context["config"]["roles"]:
try:
bundle = json.loads(
self.load_file(f"{role}.cosign-bundle", copy=True)
self.load_private_file(f"{role}.cosign-bundle", copy=True)
)
time = datetime.datetime.utcfromtimestamp(
bundle["rekorBundle"]["Payload"]["integratedTime"]
).strftime("%Y-%m-%d %H:%M:%S UTC")
present = True
except FileNotFoundError:
time = "-"
present = False
self.context["signatures"][role] = {
"time": time,
"present": present,
}

self.state = "signed"
Expand All @@ -47,7 +57,18 @@ def load_file(self, name, *, copy=False):
path = f"{self.app.srcdir}/../signature/{name}"
self.loaded_files.append(path)
if copy:
self.copiable_files.append(path)
self.copiable_files[name] = path
with open(path) as f:
return f.read()

def load_private_file(self, name, *, copy=False):
try:
uuid = self.private_files[name]
except KeyError:
raise FileNotFoundError(f"private signature file {name}")
path = f"{self.app.config.ferrocene_private_signature_files_dir}/{uuid}"
if copy:
self.copiable_files[name] = path
with open(path) as f:
return f.read()

Expand All @@ -73,33 +94,48 @@ def doctree_read(app, doctree):
def html_collect_pages(app):
# Generate the signature/index.html file.
signature = SignatureStatus(app)
yield (
"signature/index",
{"state": signature.state, "signature": signature.context},
os.path.join(os.path.dirname(__file__), "signature_page.html"),
)
if signature.state != "not_needed":
yield (
"signature/index",
{"state": signature.state, "signature": signature.context},
f"{os.path.dirname(__file__)}/signature_page/template.html",
)


def html_page_context(app, pagename, templatename, context, doctree):
# Add a variable to all Sphinx templates on whether the document is signed.
# This is used by the breadcrumbs template to decide whether to include the
# link to the signature page or not.
context["ferrocene_signed"] = app.config.ferrocene_signed
context["ferrocene_signature"] = app.config.ferrocene_signature


def build_finished(app, exception):
if exception is not None:
return

with sphinx.util.progress_message("copying signature files"):
os.makedirs(f"{app.outdir}/signature", exist_ok=True)
signature = SignatureStatus(app)
for file in signature.copiable_files:
name = os.path.basename(file)
for name, path in signature.copiable_files.items():
try:
shutil.copyfile(file, f"{app.outdir}/signature/{name}")
shutil.copyfile(path, f"{app.outdir}/signature/{name}")
except FileNotFoundError:
pass

# The JS file is written here instead of calling app.add_js_file
# because that method places the file in the _static/ output directory,
# while we want it in the signature/ output directory.
with open(f"{os.path.dirname(__file__)}/signature_page/breadcrumbs.js") as f:
breadcrumbs_js = f.read().replace(
"%%%SIGNED%%%", "true" if signature.state == "signed" else "false"
)
with open(f"{app.outdir}/signature/breadcrumbs.js", "w") as f:
f.write(breadcrumbs_js)

# Only include signatures in the qualification subset of tarballs.
with open(f"{app.outdir}/signature/ferrocene-subset", "w") as f:
f.write("signatures\n")


def setup(app):
app.connect("doctree-read", doctree_read)
Expand Down
16 changes: 16 additions & 0 deletions exts/ferrocene_qualification/signature_page/breadcrumbs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// SPDX-FileCopyrightText: The Ferrocene Developers

// Value injected by signature_page.py.
const signed = %%%SIGNED%%%;

let validElement = document.getElementById("breadcrumbs-valid-signature");
let invalidElement = document.getElementById("breadcrumbs-invalid-signature");

if (signed) {
validElement.classList.remove("hidden");
invalidElement.classList.add("hidden");
} else {
validElement.classList.add("hidden");
invalidElement.classList.remove("hidden");
}
Loading