From e7f547e850c521c6f4aed086813ec916c0e40289 Mon Sep 17 00:00:00 2001 From: clelia Date: Thu, 27 Aug 2026 17:19:10 +0200 Subject: [PATCH 1/2] feat: add a syncing job with docs --- .github/workflows/release.yml | 58 +++++++++++++++ scripts/sync_landing_docs.py | 135 ++++++++++++++++++++++++++++++++++ 2 files changed, 193 insertions(+) create mode 100755 scripts/sync_landing_docs.py diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8f798f6..fb53dab 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,3 +27,61 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }} + + sync-docs: + runs-on: ubuntu-latest + needs: release + steps: + - name: Checkout qcloud-cli + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + path: qcloud-cli + persist-credentials: false + + - name: Checkout landing_page + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: qdrant/landing_page + path: landing_page + token: ${{ secrets.LANDING_PAGE_PAT }} + persist-credentials: true + + - uses: jdx/mise-action@e6a8b3978addb5a52f2b4cd9d91eafa7f0ab959d # v4.2.0 + with: + version: 2026.3.8 + + - name: Generate command reference + working-directory: qcloud-cli + run: make docs + + - name: Sync docs into landing_page + run: | + python3 qcloud-cli/scripts/sync_landing_docs.py \ + qcloud-cli/docs/reference \ + landing_page/qdrant-landing/content/documentation/cloud-cli/reference + + - name: Open PR against landing_page + working-directory: landing_page + env: + GH_TOKEN: ${{ secrets.LANDING_PAGE_PAT }} + run: | + if git diff --quiet -- qdrant-landing/content/documentation/cloud-cli/reference; then + echo "no changes to sync" + exit 0 + fi + + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + branch="qcloud-cli-docs-sync-${{ github.ref_name }}" + git checkout -b "$branch" + git add qdrant-landing/content/documentation/cloud-cli/reference + git commit -m "docs: sync qcloud CLI command reference for ${{ github.ref_name }}" + git push -u origin "$branch" --force + + gh pr create \ + --title "docs: sync qcloud CLI command reference for ${{ github.ref_name }}" \ + --body "Auto-generated from [qdrant/qcloud-cli@${{ github.ref_name }}](https://github.com/qdrant/qcloud-cli/releases/tag/${{ github.ref_name }}) by the release workflow." \ + --head "$branch" \ + --base main \ + || echo "PR already exists for $branch" diff --git a/scripts/sync_landing_docs.py b/scripts/sync_landing_docs.py new file mode 100755 index 0000000..acc61d6 --- /dev/null +++ b/scripts/sync_landing_docs.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 + +"""Convert cobra-generated docs/reference/*.md into Hugo pages for the +landing_page repo's Qdrant Cloud CLI reference section. + +Usage: + python3 scripts/sync_landing_docs.py + + is docs/reference (output of `make docs`) + is the landing_page repo's + qdrant-landing/content/documentation/cloud-cli/reference directory + +The destination directory is fully regenerated on every run (existing +generated files are replaced, stale ones removed) so it always mirrors the +current command tree. +""" + +import re +import sys +from pathlib import Path + +LINK_RE = re.compile(r"\[([^\]]+)\]\((qcloud[\w.-]*)\.md\)") + + +def slug_for(filename: str) -> str: + """qcloud_cluster_create.md -> qcloud_cluster_create""" + return filename[:-3] + + +def rewrite_links(text: str) -> str: + def repl(match: re.Match[str]): + label, target = match.group(1), match.group(2) + if target == "qcloud": + return f"[{label}](/documentation/cloud-cli/reference/)" + return f"[{label}](/documentation/cloud-cli/reference/{target}/)" + + return LINK_RE.sub(repl, text) + + +def demote_headings(text: str) -> str: + """## -> #, ### -> ##, etc. so the page owns a single H1 title.""" + out_lines: list[str] = [] + for line in text.splitlines(): + if line.startswith("#"): + hashes = len(line) - len(line.lstrip("#")) + out_lines.append(line[1:] if hashes > 1 else line) + else: + out_lines.append(line) + return "\n".join(out_lines) + + +def annotate_code_fences(text: str) -> str: + """Bare ``` fences don't render as code blocks on the landing page; + tag every opening fence as bash (cobra only emits usage/example/flag + blocks, which are all shell-ish).""" + out_lines: list[str] = [] + in_fence = False + for line in text.splitlines(): + if line.strip() == "```": + out_lines.append("```bash" if not in_fence else "```") + in_fence = not in_fence + else: + out_lines.append(line) + return "\n".join(out_lines) + + +def convert(src_path: Path) -> tuple[str, str, str]: + """Return (title, description, body) for one generated doc file.""" + lines = src_path.read_text().splitlines() + + title_line = lines[0] + assert title_line.startswith("## "), f"unexpected heading in {src_path}" + title = title_line[3:].strip() + + description = "" + for line in lines[1:]: + stripped = line.strip() + if stripped: + description = stripped + break + + body = "\n".join(lines) + body = demote_headings(body) + body = rewrite_links(body) + body = annotate_code_fences(body) + return title, description, body + + +def frontmatter(title: str, description: str, weight: int, extra: str = "") -> str: + short = description if len(description) <= 120 else description[:117].rstrip() + "..." + return ( + "---\n" + f"title: {title}\n" + f"short_description: \"{short}\"\n" + f"description: \"{description}\"\n" + f"weight: {weight}\n" + f"{extra}" + "---\n\n" + ) + + +def main(): + if len(sys.argv) != 3: + print(__doc__) + sys.exit(1) + + src_dir = Path(sys.argv[1]) + dest_dir = Path(sys.argv[2]) + dest_dir.mkdir(parents=True, exist_ok=True) + + for existing in dest_dir.glob("*.md"): + existing.unlink() + + files = sorted(p for p in src_dir.glob("*.md")) + if not files: + print(f"no markdown files found in {src_dir}", file=sys.stderr) + sys.exit(1) + + for i, src in enumerate(files): + title, description, body = convert(src) + + if src.name == "qcloud.md": + dest = dest_dir / "_index.md" + fm = frontmatter("Command Reference", description, 0) + else: + dest = dest_dir / src.name + fm = frontmatter(title, description, i + 1) + + _ = dest.write_text(fm + body + "\n") + + print(f"wrote {len(files)} reference pages to {dest_dir}") + + +if __name__ == "__main__": + main() From 1b255b99bc5834493d90622c02f927c80845a868 Mon Sep 17 00:00:00 2001 From: clelia Date: Fri, 28 Aug 2026 11:05:24 +0200 Subject: [PATCH 2/2] fix: template injection vulnerability and too-broad permissions --- .github/workflows/release.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fb53dab..5967101 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,12 +5,13 @@ on: tags: - "v*.*.*" -permissions: - contents: write +permissions: {} jobs: release: runs-on: ubuntu-latest + permissions: + contents: write steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: @@ -64,6 +65,7 @@ jobs: working-directory: landing_page env: GH_TOKEN: ${{ secrets.LANDING_PAGE_PAT }} + REF_NAME: ${{ github.ref_name }} run: | if git diff --quiet -- qdrant-landing/content/documentation/cloud-cli/reference; then echo "no changes to sync" @@ -73,15 +75,15 @@ jobs: git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - branch="qcloud-cli-docs-sync-${{ github.ref_name }}" + branch="qcloud-cli-docs-sync-${REF_NAME}" git checkout -b "$branch" git add qdrant-landing/content/documentation/cloud-cli/reference - git commit -m "docs: sync qcloud CLI command reference for ${{ github.ref_name }}" + git commit -m "docs: sync qcloud CLI command reference for ${REF_NAME}" git push -u origin "$branch" --force gh pr create \ - --title "docs: sync qcloud CLI command reference for ${{ github.ref_name }}" \ - --body "Auto-generated from [qdrant/qcloud-cli@${{ github.ref_name }}](https://github.com/qdrant/qcloud-cli/releases/tag/${{ github.ref_name }}) by the release workflow." \ + --title "docs: sync qcloud CLI command reference for ${REF_NAME}" \ + --body "Auto-generated from [qdrant/qcloud-cli@${REF_NAME}](https://github.com/qdrant/qcloud-cli/releases/tag/${REF_NAME}) by the release workflow." \ --head "$branch" \ --base main \ || echo "PR already exists for $branch"