From ebc1d2d77bb9c8ac593ba8d5879ecbe5f44962af Mon Sep 17 00:00:00 2001 From: Harsh Mishra Date: Tue, 16 Jun 2026 16:02:18 +0530 Subject: [PATCH] add the official localstack extensions page --- .github/workflows/update-extensions-docs.yml | 73 ++++++ astro.config.mjs | 4 - scripts/generate_extensions_docs.py | 244 ++++++++++++++++++ .../tooling/extensions/official-extensions.md | 54 ++++ 4 files changed, 371 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/update-extensions-docs.yml create mode 100644 scripts/generate_extensions_docs.py create mode 100644 src/content/docs/aws/tooling/extensions/official-extensions.md diff --git a/.github/workflows/update-extensions-docs.yml b/.github/workflows/update-extensions-docs.yml new file mode 100644 index 00000000..c32a49fa --- /dev/null +++ b/.github/workflows/update-extensions-docs.yml @@ -0,0 +1,73 @@ +name: Update LocalStack Extensions Docs + +on: + schedule: + # Run on the 1st of every month at 00:00 UTC + - cron: "0 0 1 * *" + workflow_dispatch: + inputs: + targetBranch: + description: 'Target branch to create the PR against' + required: false + type: string + default: 'main' + +env: + TARGET_BRANCH: ${{ github.event.inputs.targetBranch || 'main' }} + +jobs: + update-extensions-docs: + name: Update LocalStack Extensions Docs + runs-on: ubuntu-latest + steps: + - name: Checkout docs + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + path: docs + ref: ${{ env.TARGET_BRANCH }} + + - name: Set up Python 3.11 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: "3.11" + + - name: Generate Extensions documentation + working-directory: docs + run: | + python3 scripts/generate_extensions_docs.py + env: + LOCALSTACK_AUTH_TOKEN: ${{ secrets.LOCALSTACK_AUTH_TOKEN }} + + - name: Check for changes + id: check-for-changes + working-directory: docs + env: + TARGET_BRANCH: ${{ env.TARGET_BRANCH }} + run: | + # Check if there are changed files + # Check against the PR branch if it exists, otherwise against the target branch + mkdir -p resources + FILE_TO_CHECK="src/content/docs/aws/tooling/extensions/official-extensions.md" + (git diff --name-only origin/extensions-docs-auto-updates "$FILE_TO_CHECK" 2>/dev/null || git diff --name-only "origin/$TARGET_BRANCH" "$FILE_TO_CHECK" 2>/dev/null) | tee resources/diff-check.log + echo "diff-count=$(cat resources/diff-check.log | wc -l)" >> $GITHUB_OUTPUT + cat resources/diff-check.log + + - name: Create PR + uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 + if: ${{ success() && steps.check-for-changes.outputs.diff-count != '0' && steps.check-for-changes.outputs.diff-count != '' }} + with: + path: docs + title: "Update LocalStack Extensions Documentation" + body: | + Automated update of the LocalStack Official Extensions documentation. + + This PR was auto-generated by the `update-extensions-docs` workflow which runs on the 1st of every month. + + **Changes include:** + - Updated list of official and community extensions available on the marketplace + branch: "extensions-docs-auto-updates" + author: "LocalStack Bot " + committer: "LocalStack Bot " + commit-message: "update generated LocalStack Extensions docs" + token: ${{ secrets.PRO_ACCESS_TOKEN }} diff --git a/astro.config.mjs b/astro.config.mjs index a0da1d90..71aee529 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -592,10 +592,6 @@ export default defineConfig({ directory: '/aws/tooling/extensions', }, }, - { - label: 'Official Extensions', - link: 'https://app.localstack.cloud/extensions/library/', - }, ], collapsed: true, }, diff --git a/scripts/generate_extensions_docs.py b/scripts/generate_extensions_docs.py new file mode 100644 index 00000000..e183bf7d --- /dev/null +++ b/scripts/generate_extensions_docs.py @@ -0,0 +1,244 @@ +#!/usr/bin/env python3 +""" +Script to auto-generate the LocalStack Official Extensions documentation. + +This script queries the LocalStack Platform API for the list of extensions +available on the marketplace and generates a markdown documentation page with +auto-updating tables of the official and community extensions. + +Usage: + export LOCALSTACK_AUTH_TOKEN=ls-... + python3 scripts/generate_extensions_docs.py + +Requirements: + - A valid LocalStack auth token exposed via the LOCALSTACK_AUTH_TOKEN + environment variable. +""" + +from __future__ import annotations + +import argparse +import base64 +import json +import os +import sys +import urllib.error +import urllib.request +from dataclasses import dataclass +from pathlib import Path + +# Output path for the generated documentation +DEFAULT_OUTPUT_PATH = Path("src/content/docs/aws/tooling/extensions/official-extensions.md") + +# LocalStack Platform API endpoint that returns the extensions marketplace. +MARKETPLACE_URL = "https://api.localstack.cloud/v1/extensions/marketplace" + +REQUEST_TIMEOUT = 30 + + +@dataclass +class Extension: + """Represents a single marketplace extension.""" + + name: str + display_name: str + description: str + author: str + version: str + official: bool + has_ui: bool + + @classmethod + def from_api(cls, item: dict) -> "Extension": + name = (item.get("name") or "").strip() + return cls( + name=name, + display_name=(item.get("display_name") or name or "Unknown").strip(), + description=(item.get("description") or "").strip() or "No description provided.", + author=(item.get("author") or "Unknown").strip(), + version=(item.get("version") or "").strip() or "—", + official=bool(item.get("official")), + has_ui=bool(item.get("has_ui")), + ) + + +def get_auth_token() -> str: + """Read the LocalStack auth token from the environment.""" + token = os.environ.get("LOCALSTACK_AUTH_TOKEN", "").strip() + if not token: + print( + "Error: LOCALSTACK_AUTH_TOKEN is not set. Export a valid LocalStack " + "auth token before running this script.", + file=sys.stderr, + ) + sys.exit(1) + return token + + +def fetch_marketplace(token: str) -> list[Extension]: + """Fetch the marketplace extensions from the LocalStack Platform API.""" + encoded = base64.b64encode(f":{token}".encode()).decode() + request = urllib.request.Request( + MARKETPLACE_URL, + headers={ + "Authorization": f"Basic {encoded}", + "Accept": "application/json", + }, + ) + + try: + with urllib.request.urlopen(request, timeout=REQUEST_TIMEOUT) as response: + payload = json.loads(response.read().decode()) + except urllib.error.HTTPError as exc: + if exc.code in (401, 403): + print( + "Error: Authentication failed when contacting the marketplace API. " + "Ensure LOCALSTACK_AUTH_TOKEN is set correctly.", + file=sys.stderr, + ) + else: + print(f"Error: Marketplace request failed with HTTP {exc.code}.", file=sys.stderr) + sys.exit(1) + except (urllib.error.URLError, TimeoutError) as exc: + print(f"Error: Could not reach the marketplace API: {exc}", file=sys.stderr) + sys.exit(1) + + if not isinstance(payload, list): + print("Error: Unexpected marketplace response format (expected a list).", file=sys.stderr) + sys.exit(1) + + extensions = [ + Extension.from_api(item) + for item in payload + if isinstance(item, dict) and item.get("published", True) + ] + extensions.sort(key=lambda ext: ext.display_name.lower()) + return extensions + + +def _escape_cell(value: str) -> str: + """Escape characters that would break a markdown table cell.""" + return value.replace("|", "\\|").replace("\n", " ").strip() + + +def generate_table(extensions: list[Extension]) -> list[str]: + """Generate a markdown table for a list of extensions.""" + lines = [ + "| Extension | Description | Author | Install |", + "|-----------|-------------|--------|---------|", + ] + for ext in extensions: + name = _escape_cell(ext.display_name) + if ext.has_ui: + name = f"{name} UI" + description = _escape_cell(ext.description) + author = _escape_cell(ext.author) + install = f"`localstack extensions install {ext.name}`" if ext.name else "—" + lines.append(f"| {name} | {description} | {author} | {install} |") + return lines + + +def generate_documentation(extensions: list[Extension]) -> str: + """Generate the complete markdown documentation.""" + official = [ext for ext in extensions if ext.official] + community = [ext for ext in extensions if not ext.official] + + doc_lines = [ + "---", + "title: Official Extensions", + "description: Browse the official and community LocalStack Extensions available on the marketplace.", + "template: doc", + "sidebar:", + " order: 5", + "tags: ['Hobby']", + "---", + "", + "## Introduction", + "", + "The tables below list the extensions currently available on the " + "[LocalStack marketplace](https://app.localstack.cloud/extensions/library), " + "and are kept up to date automatically.", + "", + "You can install any of the extensions below with the LocalStack CLI:", + "", + "```bash", + "localstack extensions install ", + "```", + "", + "See [Managing extensions](/aws/tooling/extensions/managing-extensions) for more details " + "on installing, listing, and removing extensions.", + "", + ":::note", + "This page is auto-generated from the LocalStack marketplace API. " + "Extensions marked with UI ship with a web UI.", + ":::", + "", + ] + + if official: + doc_lines.extend([ + "## Official Extensions", + "", + "Extensions built and maintained by the LocalStack team.", + "", + ]) + doc_lines.extend(generate_table(official)) + doc_lines.append("") + + if community: + doc_lines.extend([ + "## Community Extensions", + "", + "Extensions contributed and maintained by the LocalStack community and partners.", + "", + ]) + doc_lines.extend(generate_table(community)) + doc_lines.append("") + + return "\n".join(doc_lines) + + +def create_argument_parser() -> argparse.ArgumentParser: + """Create the argument parser for the script.""" + parser = argparse.ArgumentParser( + description="Generate the LocalStack Official Extensions documentation from the marketplace API." + ) + parser.add_argument( + "--output", + "-o", + type=Path, + default=DEFAULT_OUTPUT_PATH, + help=f"Output path for the generated documentation (default: {DEFAULT_OUTPUT_PATH})", + ) + parser.add_argument( + "--dry-run", + action="store_true", + help="Print the documentation to stdout instead of writing to file", + ) + return parser + + +def main() -> None: + """Main entry point for the script.""" + parser = create_argument_parser() + args = parser.parse_args() + + token = get_auth_token() + + print("Fetching extensions from the LocalStack marketplace...", file=sys.stderr) + extensions = fetch_marketplace(token) + print(f"Found {len(extensions)} published extensions.", file=sys.stderr) + + print("Generating documentation...", file=sys.stderr) + documentation = generate_documentation(extensions) + + if args.dry_run: + print(documentation) + else: + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(documentation.rstrip() + "\n", encoding="utf-8") + print(f"Documentation written to: {args.output}", file=sys.stderr) + + +if __name__ == "__main__": + main() diff --git a/src/content/docs/aws/tooling/extensions/official-extensions.md b/src/content/docs/aws/tooling/extensions/official-extensions.md new file mode 100644 index 00000000..252003ba --- /dev/null +++ b/src/content/docs/aws/tooling/extensions/official-extensions.md @@ -0,0 +1,54 @@ +--- +title: Official Extensions +description: Browse the official and community LocalStack Extensions available on the marketplace. +template: doc +sidebar: + order: 5 +tags: ['Hobby'] +--- + +## Introduction + +The tables below list the extensions currently available on the [LocalStack marketplace](https://app.localstack.cloud/extensions/library), and are kept up to date automatically. + +You can install any of the extensions below with the LocalStack CLI: + +```bash +localstack extensions install +``` + +See [Managing extensions](/aws/tooling/extensions/managing-extensions) for more details on installing, listing, and removing extensions. + +:::note +This page is auto-generated from the LocalStack marketplace API. Extensions marked with UI ship with a web UI. +::: + +## Official Extensions + +Extensions built and maintained by the LocalStack team. + +| Extension | Description | Author | Install | +|-----------|-------------|--------|---------| +| AWS Proxy UI | Proxy requests from your LocalStack instance to real AWS resources | LocalStack | `localstack extensions install localstack-extension-aws-proxy` | +| Diagnosis Viewer | View the diagnostics endpoint directly in localstack | LocalStack | `localstack extensions install localstack-extension-diagnosis-viewer` | +| Hello World | A minimal LocalStack extension | LocalStack | `localstack extensions install localstack-extension-hello-world` | +| httpbin UI | A simple HTTP Request & Response Service directly in LocalStack | LocalStack | `localstack extensions install localstack-extension-httpbin` | +| MailHog UI | Web and API based SMTP testing directly in LocalStack using MailHog | LocalStack | `localstack extensions install localstack-extension-mailhog` | +| Miniflare | This extension makes Miniflare (dev environment for Cloudflare workers) available directly in LocalStack | LocalStack | `localstack extensions install localstack-extension-miniflare` | +| Resource Graph UI | Altimeter based LocalStack extension that allows you to create and import into neptune a graph of the resources in your LocalStack instance | LocalStack | `localstack extensions install localstack-extension-resource-graph` | +| Stripe | A LocalStack extension that provides a mocked version of Stripe as a service | LocalStack | `localstack extensions install localstack-extension-stripe` | +| Terraform Init Hooks | Use Terraform files as initialization hooks to pre-seed your LocalStack instance automatically | Thomas Rausch | `localstack extensions install localstack-extension-terraform-init` | + +## Community Extensions + +Extensions contributed and maintained by the LocalStack community and partners. + +| Extension | Description | Author | Install | +|-----------|-------------|--------|---------| +| Authress | Add authentication, permissions, and access control to LocalStack | Authress | `localstack extensions install localstack-extension-authress` | +| Claude (Anthropic API) | LocalStack Extension for testing Anthropic Claude API integrations locally | LocalStack Team | `localstack extensions install localstack-claude` | +| Keycloak | LocalStack Extension for developing Keycloak-secured apps locally | LocalStack Team | `localstack extensions install localstack-keycloak` | +| ParadeDB | LocalStack Extension for running ParadeDB search databases locally | LocalStack & ParadeDB | `localstack extensions install localstack-paradedb` | +| TypeDB | LocalStack Extension that facilitates developing TypeDB-based applications locally. | LocalStack & TypeDB | `localstack extensions install localstack-extension-typedb` | +| WireMock | LocalStack Extension that facilitates running WireMock mock APIs locally. | LocalStack & WireMock | `localstack extensions install localstack-wiremock` | +| Xero | LocalStack Extension for developing against the Xero Finance API locally | LocalStack Team | `localstack extensions install localstack-xero` |