Skip to content

Commit

Permalink
synchronize with monorepo
Browse files Browse the repository at this point in the history
  • Loading branch information
pietroalbini committed Oct 2, 2023
1 parent ccc289e commit 401e815
Show file tree
Hide file tree
Showing 26 changed files with 285 additions and 198 deletions.
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

0 comments on commit 401e815

Please sign in to comment.