-
Notifications
You must be signed in to change notification settings - Fork 0
feat(docs): version-stamp generated SDK READMEs #41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -158,7 +158,7 @@ jobs: | |
| - name: Write top-level index | ||
| # DefaultDocumentation emits one tree per assembly. Assemble | ||
| # a small README.mdx at the top of the api/ folder listing | ||
| # the available packages. | ||
| # the available packages with their pinned versions. | ||
| # | ||
| # Emit .mdx (not .md) because Mintlify's docs.json nav | ||
| # resolver only matches .mdx for page ids; without this, | ||
|
|
@@ -171,21 +171,51 @@ jobs: | |
| # parents fails broken-links validation when the parent is | ||
| # .mdx). Users navigate to per-package pages via the URL bar | ||
| # or future programmatic _pages.json nav splice. | ||
| # | ||
| # Version next to each project is read from <Version> / | ||
| # <VersionPrefix> in the .csproj. Falls back to "unknown" so | ||
| # missing tags don't break the doc build. | ||
| run: | | ||
| { | ||
| echo "# ResQ .NET SDK" | ||
| echo "" | ||
| echo "Auto-generated reference for the public packages in" | ||
| echo "[resq-software/dotnet-sdk](https://github.com/resq-software/dotnet-sdk)." | ||
| echo "" | ||
| echo "## Packages" | ||
| echo "" | ||
| for proj in $PUBLIC_PROJECTS; do | ||
| if [ -d "$OUTPUT_DIR/$proj" ]; then | ||
| echo "- \`$proj\`" | ||
| fi | ||
| done | ||
| } > "$OUTPUT_DIR/README.mdx" | ||
| python3 - <<'PY' > "$OUTPUT_DIR/README.mdx" | ||
| import os | ||
| import pathlib | ||
| import re | ||
|
|
||
| ref_name = os.environ.get("DOCS_REF_NAME", "main") | ||
| repo = os.environ.get("GITHUB_REPOSITORY", "") | ||
| output_dir = pathlib.Path(os.environ["OUTPUT_DIR"]) | ||
| projects = (os.environ.get("PUBLIC_PROJECTS") or "").split() | ||
|
|
||
| def read_version(proj: str) -> str: | ||
| # .csproj files are usually <ProjectName>/<ProjectName>.csproj | ||
| # but DefaultDocumentation expects the assembly name as the | ||
| # output dir, so search for a .csproj matching the project. | ||
| candidates = list(pathlib.Path(".").rglob(f"{proj}.csproj")) | ||
| if not candidates: | ||
| return "unknown" | ||
| text = candidates[0].read_text(encoding="utf-8") | ||
| for tag in ("Version", "VersionPrefix"): | ||
| m = re.search(rf"<{tag}>([^<]+)</{tag}>", text) | ||
| if m: | ||
| return m.group(1).strip() | ||
| return "unknown" | ||
|
|
||
| print("# ResQ .NET SDK") | ||
| print() | ||
| print( | ||
| f"Auto-generated reference for " | ||
| f"[`{repo}`](https://github.com/{repo}) " | ||
| f"at ref `{ref_name}`." | ||
| ) | ||
| print() | ||
| print("## Packages") | ||
| print() | ||
| for proj in projects: | ||
| if not (output_dir / proj).is_dir(): | ||
| continue | ||
| version = read_version(proj) | ||
| print(f"- `{proj}` — `v{version}`") | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| PY | ||
|
|
||
| - name: Drop hash from constructor filenames + references | ||
| # DefaultDocumentation names constructor pages after the IL | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -226,7 +226,7 @@ jobs: | |
| - name: Write top-level index | ||
| # pydoc-markdown emits an overview.md per package. Stitch | ||
| # a small README.mdx at the top of the api/ folder listing | ||
| # the available packages. | ||
| # the available packages with their pinned versions. | ||
| # | ||
| # Emit .mdx (not .md) because Mintlify's docs.json nav | ||
| # resolver only matches .mdx for page ids; without this, | ||
|
|
@@ -242,24 +242,49 @@ jobs: | |
| # | ||
| # Package list is sorted alphabetically by directory name | ||
| # so it stays in sync with `_pages.json` (which | ||
| # `find ... | sort` produces below). | ||
| # `find ... | sort` produces below). The version next to | ||
| # each package is read from its pyproject.toml so the | ||
| # rendered docs can be cross-referenced against a specific | ||
| # PyPI release; the workflow ref (`DOCS_REF_NAME`) is shown | ||
| # at the top so tag-triggered runs are clearly identified. | ||
| run: | | ||
| { | ||
| echo "# ResQ Python SDK" | ||
| echo "" | ||
| echo "You can use this auto-generated reference for the public packages in" | ||
| echo "[\`${{ github.repository }}\`](https://github.com/${{ github.repository }})." | ||
| echo "" | ||
| echo "## Packages" | ||
| echo "" | ||
| for entry in $PUBLIC_PACKAGES; do | ||
| echo "${entry%%:*}" | ||
| done | sort | while read -r pkg_dir; do | ||
| if [ -d "$OUTPUT_DIR/$pkg_dir" ]; then | ||
| echo "- \`$pkg_dir\`" | ||
| fi | ||
| done | ||
| } > "$OUTPUT_DIR/README.mdx" | ||
| python3 - <<'PY' > "$OUTPUT_DIR/README.mdx" | ||
| import os | ||
| import pathlib | ||
| import re | ||
|
|
||
| ref_name = os.environ.get("DOCS_REF_NAME", "main") | ||
| repo = os.environ.get("GITHUB_REPOSITORY", "") | ||
| output_dir = pathlib.Path(os.environ["OUTPUT_DIR"]) | ||
| public = (os.environ.get("PUBLIC_PACKAGES") or "").split() | ||
|
|
||
| def read_version(pkg_dir: str) -> str: | ||
| # Best-effort version lookup so a parse failure doesn't | ||
| # break the whole doc build — fall back to "unknown". | ||
| pyproject = pathlib.Path("packages") / pkg_dir / "pyproject.toml" | ||
| if not pyproject.exists(): | ||
| return "unknown" | ||
| text = pyproject.read_text(encoding="utf-8") | ||
| m = re.search(r'^\s*version\s*=\s*"([^"]+)"', text, re.M) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| return m.group(1) if m else "unknown" | ||
|
|
||
| print("# ResQ Python SDK") | ||
| print() | ||
| print( | ||
| f"Auto-generated API reference for " | ||
| f"[`{repo}`](https://github.com/{repo}) " | ||
| f"at ref `{ref_name}`." | ||
| ) | ||
| print() | ||
| print("## Packages") | ||
| print() | ||
| entries = sorted(e.split(":", 1)[0] for e in public) | ||
| for pkg_dir in entries: | ||
| if not (output_dir / pkg_dir).is_dir(): | ||
| continue | ||
| version = read_version(pkg_dir) | ||
| print(f"- `{pkg_dir}` — `v{version}`") | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| PY | ||
|
|
||
| - name: Prefix bare-filename intra-page links with ./ | ||
| # Mintlify rejects bare-filename .md links as broken; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -130,18 +130,52 @@ jobs: | |
| # `[components/X](X/README.md)` module list down to plain | ||
| # backticked text. Users navigate to specific pages via | ||
| # the URL bar. | ||
| # | ||
| # Prepend a version banner so users browsing the rendered | ||
| # docs can identify which release the reference describes. | ||
| # Version is read from package.json at the package root; | ||
| # ref is the workflow's input ref (tag for tag-triggered | ||
| # runs). | ||
| working-directory: ${{ env.PKG_DIR }}/generated-docs | ||
| env: | ||
| PKG_REF: ${{ inputs.ref || github.ref_name }} | ||
| GH_REPO: ${{ github.repository }} | ||
| run: | | ||
| if [ -f README.md ]; then | ||
| python3 - <<'PY' | ||
| import pathlib, re | ||
| import json, os, pathlib, re | ||
|
|
||
| INTERNAL_LINK = re.compile(r'\[([^\]]+)\]\((?!https?://|mailto:|#)[^)]+\)') | ||
| p = pathlib.Path("README.md") | ||
| text = p.read_text(encoding="utf-8") | ||
| text = INTERNAL_LINK.sub(r'`\1`', text) | ||
| # Collapse accidental double-backticks from already- | ||
| # backticked link text. | ||
| text = text.replace("``", "`") | ||
|
|
||
| ref_name = os.environ.get("PKG_REF", "main") | ||
| repo = os.environ.get("GH_REPO", "") | ||
| # package.json is one level up from generated-docs. | ||
| pkg_json = pathlib.Path("..") / "package.json" | ||
| version = "unknown" | ||
| if pkg_json.exists(): | ||
| try: | ||
| version = json.loads(pkg_json.read_text(encoding="utf-8")).get("version", "unknown") | ||
| except json.JSONDecodeError: | ||
| pass | ||
|
|
||
| banner = ( | ||
| f"> **Version:** `v{version}` · **Ref:** `{ref_name}` · " | ||
| f"**Source:** [`{repo}`](https://github.com/{repo})\n\n" | ||
| ) | ||
| # Insert banner after the first H1 (typedoc emits `# ui` | ||
| # or similar). If there's no leading H1, prepend. | ||
| h1_match = re.match(r'(# [^\n]+\n+)', text) | ||
| if h1_match: | ||
| text = text[:h1_match.end()] + banner + text[h1_match.end():] | ||
| else: | ||
| text = banner + text | ||
|
Comment on lines
+167
to
+177
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are three improvements possible here:
v_display = f"v{version}" if version != "unknown" else version
banner = (
f"**Version:** {v_display} · **Ref:** {ref_name} · "
f"**Source:** [{repo}](https://github.com/{repo}/tree/{ref_name}/{pkg_dir})\\n\\n"
)
h1_match = re.search(r"(^# [^\\n]+\\n+)", text, re.M)
if h1_match:
text = text[:h1_match.end()] + banner + text[h1_match.end():]
else:
text = banner + textReferences
|
||
|
|
||
| pathlib.Path("README.mdx").write_text(text, encoding="utf-8") | ||
| p.unlink() | ||
| PY | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The regex for parsing .csproj tags is a bit fragile as it doesn't account for potential XML attributes on the tag (e.g., ). Using a more robust pattern would ensure the version is captured even in more complex project files.
m = re.search(rf"<{tag}\\b[^>]*>([^<]+)</{tag}>", text)