Skip to content

Commit 183e059

Browse files
authored
Update developer guide cover
1 parent ade4f43 commit 183e059

File tree

4 files changed

+170
-10
lines changed

4 files changed

+170
-10
lines changed

.github/workflows/developer-guide-docs.yml

Lines changed: 93 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ name: Build Developer Guide Docs
22

33
on:
44
pull_request:
5+
types: [opened, synchronize, reopened, ready_for_review]
56
paths:
67
- 'docs/developer-guide/**'
78
- '.github/workflows/developer-guide-docs.yml'
@@ -21,23 +22,60 @@ jobs:
2122
- name: Check out repository
2223
uses: actions/checkout@v4
2324

24-
- name: Update publication metadata for release
25-
if: ${{ github.event_name == 'release' && github.event.action == 'published' }}
26-
env:
27-
RELEASE_TAG: ${{ github.event.release.tag_name }}
25+
- name: Determine publication metadata
2826
run: |
2927
set -euo pipefail
3028
REV_DATE="$(date -u +%Y-%m-%d)"
31-
VERSION="${RELEASE_TAG:-}"
29+
VERSION="${GITHUB_HEAD_REF:-}"
3230
if [ -z "$VERSION" ] && [ -n "${GITHUB_REF_NAME:-}" ]; then
3331
VERSION="$GITHUB_REF_NAME"
3432
fi
33+
if [ -z "$VERSION" ] && [ -n "${GITHUB_SHA:-}" ]; then
34+
VERSION="${GITHUB_SHA:0:7}"
35+
fi
3536
VERSION="${VERSION#v}"
3637
if [ -z "$VERSION" ]; then
3738
VERSION="UNKNOWN"
3839
fi
39-
sed -i -E "s/^:revdate: .*/:revdate: ${REV_DATE}/" docs/developer-guide/developer-guide.asciidoc
40-
sed -i -E "s/^:revnumber: .*/:revnumber: ${VERSION}/" docs/developer-guide/developer-guide.asciidoc
40+
if date -u -d "$REV_DATE" '+%B %-d, %Y' >/tmp/rev_human 2>/dev/null; then
41+
REV_HUMAN_DATE="$(cat /tmp/rev_human)"
42+
else
43+
REV_HUMAN_DATE="$(date -u '+%B %-d, %Y')"
44+
fi
45+
echo "Developer guide workflow metadata:" >&2
46+
echo " REV_DATE=$REV_DATE" >&2
47+
echo " REV_NUMBER=$VERSION" >&2
48+
echo " REV_HUMAN_DATE=$REV_HUMAN_DATE" >&2
49+
{
50+
echo "REV_DATE=$REV_DATE"
51+
echo "REV_NUMBER=$VERSION"
52+
echo "REV_HUMAN_DATE=$REV_HUMAN_DATE"
53+
} >> "$GITHUB_ENV"
54+
55+
- name: Render publication cover artwork
56+
run: |
57+
set -euo pipefail
58+
sudo apt-get update
59+
sudo apt-get install -y --no-install-recommends librsvg2-bin
60+
SOURCE="docs/developer-guide/book-cover.svg"
61+
GENERATED_SVG="docs/developer-guide/book-cover.generated.svg"
62+
GENERATED_PNG="docs/developer-guide/book-cover.generated.png"
63+
cp "$SOURCE" "$GENERATED_SVG"
64+
export GENERATED_SVG
65+
python3 scripts/developer-guide/prepare_cover_artwork.py \
66+
"$GENERATED_SVG" \
67+
--rev-number "$REV_NUMBER" \
68+
--rev-human-date "$REV_HUMAN_DATE" \
69+
--rev-date "$REV_DATE"
70+
echo "Rasterizing cover artwork to ${GENERATED_PNG}" >&2
71+
rsvg-convert -w 2551 -h 3579 "$GENERATED_SVG" -o "$GENERATED_PNG"
72+
ls -l "$GENERATED_PNG"
73+
file "$GENERATED_PNG"
74+
{
75+
echo "COVER_IMAGE_ATTR=$(basename "$GENERATED_PNG")"
76+
echo "GENERATED_COVER_IMAGE=$GENERATED_PNG"
77+
echo "GENERATED_COVER_SVG=$GENERATED_SVG"
78+
} >> "$GITHUB_ENV"
4179
4280
- name: Set up Ruby
4381
uses: ruby/setup-ruby@v1
@@ -55,19 +93,47 @@ jobs:
5593
HTML_BUILD_DIR="${OUTPUT_ROOT}/html"
5694
PDF_BUILD_DIR="${OUTPUT_ROOT}/pdf"
5795
PACKAGE_DIR="${OUTPUT_ROOT}/html-package"
96+
COVER_IMAGE_ATTR_VALUE="${COVER_IMAGE_ATTR:-book-cover.svg}"
97+
GENERATED_COVER_IMAGE="${GENERATED_COVER_IMAGE:-}"
98+
GENERATED_COVER_SVG="${GENERATED_COVER_SVG:-}"
99+
echo "Building with cover image attribute: ${COVER_IMAGE_ATTR_VALUE}" >&2
58100
mkdir -p "$HTML_BUILD_DIR" "$PDF_BUILD_DIR"
59-
asciidoctor -D "$HTML_BUILD_DIR" -o developer-guide.html docs/developer-guide/developer-guide.asciidoc
60-
asciidoctor-pdf -D "$PDF_BUILD_DIR" -o developer-guide.pdf docs/developer-guide/developer-guide.asciidoc
101+
asciidoctor \
102+
-a revdate="$REV_DATE" \
103+
-a revnumber="$REV_NUMBER" \
104+
-a cover-image="$COVER_IMAGE_ATTR_VALUE" \
105+
-D "$HTML_BUILD_DIR" \
106+
-o developer-guide.html \
107+
docs/developer-guide/developer-guide.asciidoc
108+
asciidoctor-pdf \
109+
-a revdate="$REV_DATE" \
110+
-a revnumber="$REV_NUMBER" \
111+
-a cover-image="$COVER_IMAGE_ATTR_VALUE" \
112+
-D "$PDF_BUILD_DIR" \
113+
-o developer-guide.pdf \
114+
docs/developer-guide/developer-guide.asciidoc
61115
rm -rf "$PACKAGE_DIR"
62116
mkdir -p "$PACKAGE_DIR"
117+
if [ -n "$GENERATED_COVER_IMAGE" ] && [ -f "$GENERATED_COVER_IMAGE" ]; then
118+
cp "$GENERATED_COVER_IMAGE" "$HTML_BUILD_DIR/$(basename "$GENERATED_COVER_IMAGE")"
119+
fi
63120
cp "$HTML_BUILD_DIR/developer-guide.html" "$PACKAGE_DIR/"
64121
for asset_dir in docs/developer-guide/*; do
65122
base_name="$(basename "$asset_dir")"
66123
if [ -d "$asset_dir" ] && [ "$base_name" != "sketch" ]; then
67124
cp -R "$asset_dir" "$PACKAGE_DIR/"
68125
fi
69126
done
127+
if [ -n "$GENERATED_COVER_IMAGE" ] && [ -f "$GENERATED_COVER_IMAGE" ]; then
128+
cp "$GENERATED_COVER_IMAGE" "$PACKAGE_DIR/$(basename "$GENERATED_COVER_IMAGE")"
129+
fi
70130
(cd "$PACKAGE_DIR" && zip -r "../developer-guide-html.zip" .)
131+
if [ -n "$GENERATED_COVER_IMAGE" ] && [ -f "$GENERATED_COVER_IMAGE" ]; then
132+
rm -f "$GENERATED_COVER_IMAGE"
133+
fi
134+
if [ -n "$GENERATED_COVER_SVG" ] && [ -f "$GENERATED_COVER_SVG" ]; then
135+
rm -f "$GENERATED_COVER_SVG"
136+
fi
71137
72138
- name: Upload HTML artifact
73139
uses: actions/upload-artifact@v4
@@ -92,6 +158,24 @@ jobs:
92158
const marker = '<!-- developer-guide-artifacts -->';
93159
const { owner, repo } = context.repo;
94160
const runId = context.runId;
161+
const prNumber = context.payload.pull_request?.number;
162+
const runHeadSha = context.payload.pull_request?.head?.sha;
163+
164+
if (prNumber && runHeadSha) {
165+
const { data: pr } = await github.rest.pulls.get({
166+
owner,
167+
repo,
168+
pull_number: prNumber
169+
});
170+
171+
if (pr.head.sha !== runHeadSha) {
172+
console.log(
173+
`Skipping PR comment update because workflow run head ${runHeadSha} is not the latest PR head ${pr.head.sha}.`
174+
);
175+
return;
176+
}
177+
}
178+
95179
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
96180
owner,
97181
repo,

docs/developer-guide/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
book-cover.generated.svg
2+
book-cover.generated.png

docs/developer-guide/developer-guide.asciidoc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
:autofit-option:
1717
//:hide-uri-scheme:
1818
:uuid: 92CA37B2-EB2B-4B8F-AC7C-1ED13683F7FB
19-
:front-cover-image: image:img/cover.png[Front Cover,1000,1600]
19+
:cover-image: book-cover.svg
20+
:front-cover-image: image:{cover-image}[Front Cover]
2021
:lang: en-US
2122
:revdate: 2025-10-20
2223
:revnumber: DEV-SNAPSHOT
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#!/usr/bin/env python3
2+
"""Prepare developer guide cover artwork for rasterization."""
3+
4+
from __future__ import annotations
5+
6+
import argparse
7+
import pathlib
8+
import re
9+
import sys
10+
11+
12+
VERSION_PATTERN = re.compile(r"Version\s+[^<]+")
13+
CSS_VARIABLE_DECL_PATTERN = re.compile(r"--([\w-]+):\s*([^;]+);")
14+
CSS_VARIABLE_REF_PATTERN = re.compile(r"var\(--([\w-]+)\)")
15+
16+
17+
def parse_args(argv: list[str]) -> argparse.Namespace:
18+
parser = argparse.ArgumentParser(description=__doc__)
19+
parser.add_argument("svg_path", help="Path to the SVG file to update")
20+
parser.add_argument("--rev-number", required=True, help="Revision number to embed")
21+
parser.add_argument("--rev-human-date", default="", help="Human readable revision date")
22+
parser.add_argument("--rev-date", default="", help="Fallback machine readable revision date")
23+
return parser.parse_args(argv)
24+
25+
26+
def inject_version_label(svg_text: str, version_label: str) -> str:
27+
updated_text, count = VERSION_PATTERN.subn(version_label, svg_text, count=1)
28+
if count != 1:
29+
raise RuntimeError("Could not find version text placeholder in cover artwork")
30+
return updated_text
31+
32+
33+
def resolve_css_variables(svg_text: str) -> tuple[str, int]:
34+
variables = {name: value.strip() for name, value in CSS_VARIABLE_DECL_PATTERN.findall(svg_text)}
35+
replacement_count = 0
36+
37+
def replace(match: re.Match[str]) -> str:
38+
nonlocal replacement_count
39+
name = match.group(1)
40+
value = variables.get(name)
41+
if value is None:
42+
return match.group(0)
43+
replacement_count += 1
44+
return value
45+
46+
resolved_text = CSS_VARIABLE_REF_PATTERN.sub(replace, svg_text)
47+
return resolved_text, replacement_count
48+
49+
50+
def main(argv: list[str]) -> int:
51+
args = parse_args(argv)
52+
53+
svg_path = pathlib.Path(args.svg_path)
54+
svg_text = svg_path.read_text(encoding="utf-8")
55+
56+
revision_date = args.rev_human_date or args.rev_date
57+
version_label = f"Version {args.rev_number}".strip()
58+
if revision_date:
59+
version_label = f"{version_label} - {revision_date.strip()}"
60+
61+
svg_text = inject_version_label(svg_text, version_label)
62+
svg_text, replacement_count = resolve_css_variables(svg_text)
63+
64+
svg_path.write_text(svg_text, encoding="utf-8")
65+
66+
print(f"Injected cover legend: {version_label}")
67+
print(f"Resolved {replacement_count} CSS variable references for rasterization")
68+
69+
return 0
70+
71+
72+
if __name__ == "__main__":
73+
sys.exit(main(sys.argv[1:]))

0 commit comments

Comments
 (0)