From cc2071110a2bcb05884f3d949a9a58d70acdfbd9 Mon Sep 17 00:00:00 2001 From: Jan Kadlec Date: Tue, 17 Mar 2026 14:17:38 +0100 Subject: [PATCH 1/2] fix(docs): fix docs build for old branches and broken links Several issues with the V2 docs pipeline building old release branches: 1. Always use griffe_builder.py and method_page_renderer.py from the current branch instead of checking if they exist on the target branch. The scripts were always executed from the current branch anyway, and griffe does static analysis via --search-path (no imports needed). This removes the json_builder.py fallback that caused ModuleNotFoundError and the pip install -e workaround. 2. Restore Hugo shortcode templates (parameters-block, parameter) that were deleted in the griffe pipeline commit. Old release branches have hand-written method pages using these shortcodes without api_ref frontmatter, so method_page_renderer skips them and Hugo needs the templates to render them. 3. Fix python_ref_builder generating only one page for modules/classes sharing a leaf name (e.g. declarative_model under data_source, workspace, user, permission). The "if name not in links" guard prevented page creation for all but the first. Now all modules and classes always get their pages while the links dict keeps first-wins for type-annotation resolution. 4. Use relative links (href="child_name/") for module child listings and class method/property tables instead of the global links dict. This fixes cross-links where e.g. load_from_disk in CatalogDeclarativeDataSources pointed to workspace's version. 5. Temporarily disable version caching while stabilizing the pipeline. JIRA: trivial risk: nonprod --- .github/workflows/netlify-deploy-v2.yaml | 28 +++++------ docs/layouts/shortcodes/parameter.html | 5 ++ docs/layouts/shortcodes/parameters-block.html | 20 ++++++++ scripts/docs/python_ref_builder.py | 50 ++++++++++--------- scripts/generate-single-version.sh | 34 ++++++------- scripts/generate.sh | 29 +++++------ 6 files changed, 93 insertions(+), 73 deletions(-) create mode 100644 docs/layouts/shortcodes/parameter.html create mode 100644 docs/layouts/shortcodes/parameters-block.html diff --git a/.github/workflows/netlify-deploy-v2.yaml b/.github/workflows/netlify-deploy-v2.yaml index cb9422fc8..2fc65469e 100644 --- a/.github/workflows/netlify-deploy-v2.yaml +++ b/.github/workflows/netlify-deploy-v2.yaml @@ -39,39 +39,37 @@ jobs: SHA=$(gh api "repos/${{ github.repository }}/git/ref/heads/${{ matrix.version.branch }}" -q '.object.sha') echo "sha=$SHA" >> $GITHUB_OUTPUT echo "Branch ${{ matrix.version.branch }} -> section ${{ matrix.version.section }} (SHA: $SHA)" - - name: Cache version docs - id: cache - uses: actions/cache@v4 - with: - path: docs/versioned_docs/${{ matrix.version.section }} - key: version-docs-${{ hashFiles('scripts/docs/*.py', 'scripts/docs/templates/**', 'docs/*_template.md') }}-${{ matrix.version.section }}-${{ steps.sha.outputs.sha }} + # TODO: re-enable once the pipeline is stable + # - name: Cache version docs + # id: cache + # uses: actions/cache@v4 + # with: + # path: docs/versioned_docs/${{ matrix.version.section }} + # key: version-docs-${{ hashFiles('scripts/docs/*.py', 'scripts/docs/templates/**', 'docs/*_template.md') }}-${{ matrix.version.section }}-${{ steps.sha.outputs.sha }} - name: Checkout - if: steps.cache.outputs.cache-hit != 'true' + uses: actions/checkout@v4 - name: Fetch target branch - if: steps.cache.outputs.cache-hit != 'true' + run: git fetch origin ${{ matrix.version.branch }} - name: Checkout branch packages - if: steps.cache.outputs.cache-hit != 'true' + run: | git checkout origin/${{ matrix.version.branch }} -- gooddata-api-client/ packages/gooddata-sdk/ packages/gooddata-pandas/ - name: Setup Python - if: steps.cache.outputs.cache-hit != 'true' + uses: actions/setup-python@v5 with: python-version-file: ".python-version" cache: 'pip' cache-dependency-path: scripts/script-requirements.txt - name: Install Dependencies - if: steps.cache.outputs.cache-hit != 'true' + run: | python -m pip install --upgrade pip pip install -r scripts/script-requirements.txt - # json_builder.py (legacy fallback for branches without griffe_builder.py) - # imports gooddata_sdk and gooddata_pandas at runtime, so they must be installed. - pip install -e gooddata-api-client/ -e packages/gooddata-sdk/ -e packages/gooddata-pandas/ - name: Generate version docs - if: steps.cache.outputs.cache-hit != 'true' + run: bash scripts/generate-single-version.sh "origin/${{ matrix.version.branch }}" "${{ matrix.version.section }}" - name: Upload version artifact uses: actions/upload-artifact@v4 diff --git a/docs/layouts/shortcodes/parameter.html b/docs/layouts/shortcodes/parameter.html new file mode 100644 index 000000000..8912e9d21 --- /dev/null +++ b/docs/layouts/shortcodes/parameter.html @@ -0,0 +1,5 @@ + +{{ with .Get "p_name" }}{{ . | safeHTML }}{{ end }} +{{ with .Get "p_type" }}{{ . | safeHTML }}{{ end }} +{{.Inner | safeHTML}} + diff --git a/docs/layouts/shortcodes/parameters-block.html b/docs/layouts/shortcodes/parameters-block.html new file mode 100644 index 000000000..c3e332a48 --- /dev/null +++ b/docs/layouts/shortcodes/parameters-block.html @@ -0,0 +1,20 @@ +{{ $partitle := .Get "title"}} +

{{$partitle}}

+{{ with .Get `None`}} +None +{{else}} + + + +{{ if eq $partitle "Parameters"}} + +{{end}} + + + + + +{{.Inner}} + +
nametypedescription
+{{end}} diff --git a/scripts/docs/python_ref_builder.py b/scripts/docs/python_ref_builder.py index 3e383aceb..f90fc6d65 100644 --- a/scripts/docs/python_ref_builder.py +++ b/scripts/docs/python_ref_builder.py @@ -242,12 +242,14 @@ def render_class_html(class_data: dict, parent_name: str, import_path: str, reso continue fds = fdata.get("docstring_parsed") desc = resolver.all_links(fds.get("short_description", "")) if fds else "" + # Link to the method's own child page (relative), not the global links dict + local_link = f'{fname}' if fdata.get("is_property"): - properties.append({"name_link": resolver.type_link(fname), "description": desc}) + properties.append({"name_link": local_link, "description": desc}) else: methods.append( { - "name_link": resolver.type_link(fname), + "name_link": local_link, "signature": _function_signature(fdata), "description": desc, } @@ -263,7 +265,9 @@ def render_module_html(module_data: dict, resolver: LinkResolver) -> str: for obj_name, obj_data in module_data.items(): if obj_name == "kind" or not isinstance(obj_data, dict): continue - entries.append({"kind": obj_data.get("kind", ""), "name_link": resolver.type_link(obj_name)}) + # Link to the child page (relative), not the global links dict + local_link = f'{obj_name}' + entries.append({"kind": obj_data.get("kind", ""), "name_link": local_link}) return _MODULE_TPL.render(entries=entries) @@ -333,34 +337,34 @@ def _pass1(data_root: dict, dir_root: Path, api_ref_root: str, module_import_pat obj_module_import_path = obj_module_import_path.replace(".functions", "") if kind == "module": + (dir_root / name).mkdir(exist_ok=True) if name not in links: - (dir_root / name).mkdir(exist_ok=True) links[name] = {"path": f"{api_ref_root}/{name}".lower(), "kind": "function"} - pages.append( - _PageSpec( - kind="module", - name=name, - parent_name="", - import_path=obj_module_import_path, - file_path=dir_root / name / "_index.md", - data=obj, - ) + pages.append( + _PageSpec( + kind="module", + name=name, + parent_name="", + import_path=obj_module_import_path, + file_path=dir_root / name / "_index.md", + data=obj, ) + ) elif kind == "class": + (dir_root / name).mkdir(exist_ok=True) if name not in links: - (dir_root / name).mkdir(exist_ok=True) links[name] = {"path": f"{api_ref_root}/{name}".lower(), "kind": "class"} - pages.append( - _PageSpec( - kind="class", - name=name, - parent_name=module_import_path.split(".")[-1], - import_path=obj_module_import_path, - file_path=dir_root / name / "_index.md", - data=obj, - ) + pages.append( + _PageSpec( + kind="class", + name=name, + parent_name=module_import_path.split(".")[-1], + import_path=obj_module_import_path, + file_path=dir_root / name / "_index.md", + data=obj, ) + ) elif name == "functions": for func_name in obj: diff --git a/scripts/generate-single-version.sh b/scripts/generate-single-version.sh index 3d86130d6..828695d61 100755 --- a/scripts/generate-single-version.sh +++ b/scripts/generate-single-version.sh @@ -54,31 +54,27 @@ if git cat-file -e "$GRIFFE_GEN_FILE" 2>/dev/null || git cat-file -e "$LEGACY_GE rm -f api_spec.toml fi - # Generate API introspection data — prefer griffe (static analysis, no imports needed) - if git cat-file -e "$GRIFFE_GEN_FILE" 2>/dev/null; then - echo "Using griffe_builder.py (static analysis)" - python3 ../scripts/docs/griffe_builder.py \ - --search-path ../packages/gooddata-sdk/src \ - --search-path ../packages/gooddata-pandas/src \ - --output data.json \ - gooddata_sdk gooddata_pandas - else - echo "Falling back to json_builder.py (runtime introspection)" - python3 ../scripts/docs/json_builder.py - fi + # Generate API introspection data using griffe (static analysis, no imports needed). + # Always use the current branch's griffe_builder.py — it works on any branch's + # source code via --search-path and doesn't require the SDK packages to be installed. + python3 ../scripts/docs/griffe_builder.py \ + --search-path ../packages/gooddata-sdk/src \ + --search-path ../packages/gooddata-pandas/src \ + --output data.json \ + gooddata_sdk gooddata_pandas # Generate API reference markdown files and export links for method page renderer python3 ../scripts/docs/python_ref_builder.py api_spec.toml \ data.json "$section" "$content_dir" \ --export-links links.json - # Pre-render method pages with api_ref directives - if git cat-file -e "$branch:scripts/docs/method_page_renderer.py" 2>/dev/null; then - echo "Pre-rendering method pages for section $section..." - python3 ../scripts/docs/method_page_renderer.py \ - data.json "$content_dir/$section" \ - --links-json links.json - fi + # Pre-render method pages with api_ref directives. + # Always use the current branch's renderer — old branches have Hugo shortcodes + # (parameters-block, parameter) whose templates were removed. + echo "Pre-rendering method pages for section $section..." + python3 ../scripts/docs/method_page_renderer.py \ + data.json "$content_dir/$section" \ + --links-json links.json # Clean up intermediate files (no longer needed after pre-rendering) rm -f data.json links.json diff --git a/scripts/generate.sh b/scripts/generate.sh index 3a208b4a8..5796daf0c 100755 --- a/scripts/generate.sh +++ b/scripts/generate.sh @@ -103,24 +103,21 @@ for branch in "${branches_to_process[@]}" ; do echo "removing the API_spec" rm -rf api_spec.toml fi - # Prefer griffe (static analysis, no imports needed) - if git cat-file -e "$GRIFFE_GEN_FILE" 2>/dev/null; then - python3 ../scripts/docs/griffe_builder.py \ - --search-path ../packages/gooddata-sdk/src \ - --search-path ../packages/gooddata-pandas/src \ - --output data.json \ - gooddata_sdk gooddata_pandas - else - python3 ../scripts/docs/json_builder.py - fi + # Always use griffe (static analysis, no imports needed). + # Works on any branch's source code via --search-path. + python3 ../scripts/docs/griffe_builder.py \ + --search-path ../packages/gooddata-sdk/src \ + --search-path ../packages/gooddata-pandas/src \ + --output data.json \ + gooddata_sdk gooddata_pandas python3 ../scripts/docs/python_ref_builder.py api_spec.toml data.json "$target_section" versioned_docs \ --export-links links.json - # Pre-render method pages with api_ref directives - if git cat-file -e "$branch:scripts/docs/method_page_renderer.py" 2>/dev/null; then - python3 ../scripts/docs/method_page_renderer.py \ - data.json "versioned_docs/$target_section" \ - --links-json links.json - fi + # Pre-render method pages with api_ref directives. + # Always use the current branch's renderer — old branches have Hugo shortcodes + # (parameters-block, parameter) whose templates were removed. + python3 ../scripts/docs/method_page_renderer.py \ + data.json "versioned_docs/$target_section" \ + --links-json links.json rm -f data.json links.json fi fi From 63594e97d5d03d12247b59b914dcd6f5e5bf97bf Mon Sep 17 00:00:00 2001 From: Jan Kadlec Date: Tue, 17 Mar 2026 15:41:32 +0100 Subject: [PATCH 2/2] fix(docs): re-enable version caching in V2 deploy Re-enable the version docs cache with an updated key that includes docs/layouts/shortcodes/** so shortcode template changes bust the cache. JIRA: trivial risk: nonprod --- .github/workflows/netlify-deploy-v2.yaml | 25 ++++++++++++------------ 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/.github/workflows/netlify-deploy-v2.yaml b/.github/workflows/netlify-deploy-v2.yaml index 2fc65469e..a1886d5e1 100644 --- a/.github/workflows/netlify-deploy-v2.yaml +++ b/.github/workflows/netlify-deploy-v2.yaml @@ -39,37 +39,36 @@ jobs: SHA=$(gh api "repos/${{ github.repository }}/git/ref/heads/${{ matrix.version.branch }}" -q '.object.sha') echo "sha=$SHA" >> $GITHUB_OUTPUT echo "Branch ${{ matrix.version.branch }} -> section ${{ matrix.version.section }} (SHA: $SHA)" - # TODO: re-enable once the pipeline is stable - # - name: Cache version docs - # id: cache - # uses: actions/cache@v4 - # with: - # path: docs/versioned_docs/${{ matrix.version.section }} - # key: version-docs-${{ hashFiles('scripts/docs/*.py', 'scripts/docs/templates/**', 'docs/*_template.md') }}-${{ matrix.version.section }}-${{ steps.sha.outputs.sha }} + - name: Cache version docs + id: cache + uses: actions/cache@v4 + with: + path: docs/versioned_docs/${{ matrix.version.section }} + key: version-docs-${{ hashFiles('scripts/docs/*.py', 'scripts/docs/templates/**', 'docs/*_template.md', 'docs/layouts/shortcodes/**') }}-${{ matrix.version.section }}-${{ steps.sha.outputs.sha }} - name: Checkout - + if: steps.cache.outputs.cache-hit != 'true' uses: actions/checkout@v4 - name: Fetch target branch - + if: steps.cache.outputs.cache-hit != 'true' run: git fetch origin ${{ matrix.version.branch }} - name: Checkout branch packages - + if: steps.cache.outputs.cache-hit != 'true' run: | git checkout origin/${{ matrix.version.branch }} -- gooddata-api-client/ packages/gooddata-sdk/ packages/gooddata-pandas/ - name: Setup Python - + if: steps.cache.outputs.cache-hit != 'true' uses: actions/setup-python@v5 with: python-version-file: ".python-version" cache: 'pip' cache-dependency-path: scripts/script-requirements.txt - name: Install Dependencies - + if: steps.cache.outputs.cache-hit != 'true' run: | python -m pip install --upgrade pip pip install -r scripts/script-requirements.txt - name: Generate version docs - + if: steps.cache.outputs.cache-hit != 'true' run: bash scripts/generate-single-version.sh "origin/${{ matrix.version.branch }}" "${{ matrix.version.section }}" - name: Upload version artifact uses: actions/upload-artifact@v4