Skip to content

Update website generator and container image tag helper scripts - #236

Merged
bubacoder merged 2 commits into
mainfrom
feature/helper-scripts
Dec 1, 2025
Merged

Update website generator and container image tag helper scripts#236
bubacoder merged 2 commits into
mainfrom
feature/helper-scripts

Conversation

@bubacoder

@bubacoder bubacoder commented Nov 29, 2025

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Added version-aware sorting for container image tags with a new --sort option (version, updated, default)
    • Export command now defaults to stdout when no output file is specified
  • Documentation

    • Updated build and usage docs and corrected generator / wording in comments
    • Added a task to generate a YAML service list (creates services.yaml when run)

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Nov 29, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Replaces several relative imports with absolute imports in docs/web, switches invocation of the update-docs script to a standalone script path, enhances export-services to support stdout/file output, adds a Taskfile task, and implements version- and date-aware sorting with a new --sort option for container tag retrieval.

Changes

Cohort / File(s) Summary
Import conversions
docs/web/docker_scanner.py, docs/web/update-docs.py, docs/web/export-services.py
Converted relative imports (e.g., .compose_processor) to absolute imports (e.g., compose_processor) across docs/web modules.
Script invocation & docs
docs/web/Dockerfile, docs/web/README.md, docs/web/.gitignore
Changed execution from python -m docs.web.update_docs / update_docs.py to running the standalone script /repo/docs/web/update-docs.py and updated references/comments.
Package cleanup
docs/web/__init__.py
Removed the module-level docstring.
Export services output behavior
docs/web/export-services.py, docs/web/Taskfile.web.yaml
export-services now defaults to stdout when no --output-file provided, writes YAML either to stdout or to a file, updated docstring/CLI help; new Taskfile task create-service-list added to run export-services to docs/services.yaml.
Container tag sorting & CLI
scripts/infra-mcp/tools/get_container_tags.py
Added version- and datetime-parsing helpers, new sort keys (_version_sort_key, _sort_tags), updated fetchers (get_docker_hub_tags, get_registry_tags) and get_image_tags to accept sort_by, and added --sort CLI option (choices: version, updated, default).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Inspect absolute import changes for resolution issues or unintended name collisions
  • Verify Dockerfile script path and Taskfile invocation work in build contexts
  • Review export-services' stdout vs file output paths for identical YAML formatting and logging
  • Validate tag parsing/sorting edge cases (non-version tags, timestamps, 'latest')

Possibly related PRs

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main changes: updates to website generator scripts (docs/web/) and container image tag helper scripts (scripts/infra-mcp/tools/). It is concise, specific, and clearly conveys the primary focus of the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 84.62% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/helper-scripts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
scripts/infra-mcp/tools/get_container_tags.py (1)

216-284: Same unused limit parameter issue as in get_docker_hub_tags.

This method has the same unused limit parameter issue. For consistency, apply the same solution chosen for get_docker_hub_tags (see previous comment at lines 144-214).

🧹 Nitpick comments (7)
scripts/infra-mcp/tools/get_container_tags.py (2)

88-122: Consider consistent error handling for datetime parsing.

Line 114 parses last_updated without a try-except block, while lines 119-121 wrap similar parsing logic in error handling. Although the conditional check provides some protection, an unexpected datetime format could still cause an exception.

Apply this diff to add consistent error handling:

         # Priority 2: Version tags
         version = self._parse_version(tag['name'])
         if version:
             # Pad version tuple to 4 elements for consistent comparison
             padded_version = version + (0,) * (4 - len(version))
-            updated = datetime.fromisoformat(tag['last_updated'].replace('Z', '+00:00')) if tag.get('last_updated') else datetime.min
+            try:
+                updated = datetime.fromisoformat(tag['last_updated'].replace('Z', '+00:00')) if tag.get('last_updated') else datetime.min
+            except (ValueError, AttributeError):
+                updated = datetime.min
             return (1, padded_version[:4], updated)

144-214: Address unused limit parameter flagged by static analysis.

The limit parameter is documented as "unused in fetching, used by caller" but triggers a linter warning. Consider either:

  1. Removing the parameter and letting callers handle limiting (cleaner API)
  2. Applying the limit after sorting (lines 208-209)
  3. Prefixing with underscore _limit to indicate intentionally unused

The current approach of fetching all tags and sorting before limiting is reasonable, but having an unused parameter in the signature may confuse callers.

Option 1: Remove the parameter

-    def get_docker_hub_tags(self, image_name: str, limit: int = 10, architecture: str = "linux/amd64", sort_by: str = "version") -> list[dict[str, Any]]:
+    def get_docker_hub_tags(self, image_name: str, architecture: str = "linux/amd64", sort_by: str = "version") -> list[dict[str, Any]]:
         """Query Docker Hub for image tags with timestamp information.

         Args:
             image_name: Name of the image to query
-            limit: Maximum number of tags to return (unused in fetching, used by caller)
             architecture: Architecture to filter by (e.g., 'linux/amd64')
             sort_by: Sort method - 'version' (default), 'updated', or 'default' (Docker Hub order)

Option 2: Apply limit after sorting

             # Sort based on sort_by parameter
             self._sort_tags(tag_data, sort_by)
+            # Apply limit if specified
+            if limit:
+                tag_data = tag_data[:limit]
docs/web/.gitignore (1)

4-4: Minor wording nit in generator comment

Consider changing “based the content” to “based on the content” for clarity:

-# Generated by update-docs.py, based the content in <repo-root>/docker/ and additional locations
+# Generated by update-docs.py, based on the content in <repo-root>/docker/ and additional locations
docs/web/docker_scanner.py (1)

7-7: Local import change may affect package-style usage

Switching to from compose_processor import ComposeFileProcessor works when docs/web is on sys.path as a plain directory (e.g., running update-docs.py directly), but will fail if this module is imported as part of a package, e.g. from docs.web.docker_scanner import DockerComposeScanner, unless compose_processor is also available as a top‑level module.

If you still support docs.web as an importable package anywhere (tests, tools, or other code), consider either:

  • using a package-qualified import (e.g., from docs.web.compose_processor import ComposeFileProcessor), and running via python -m; or
  • confirming that these modules are only ever used via the standalone scripts and not imported as a package.
docs/web/update-docs.py (1)

11-13: Direct local imports tie this script to “run as file” usage

Using bare imports (from docker_scanner import ..., etc.) is compatible with invoking update-docs.py directly (as in the Dockerfile), since the script directory is on sys.path. However, this will break if DocsProcessor is ever imported from a package context (e.g., from docs.web.update_docs import DocsProcessor), because these modules won’t resolve as top‑level packages there.

If you still need package-style usage anywhere, consider:

  • reverting to package-relative imports and running via python -m, or
  • fully qualifying the imports with the package name and ensuring docs/web is a proper package.

Otherwise, if the contract is “script only”, this change is consistent—just worth confirming that nothing else relies on importing these modules.

docs/web/Dockerfile (1)

11-11: Consider invoking the generator via python for robustness

Calling /repo/docs/web/update-docs.py directly relies on the script being executable and the shebang staying correct. To avoid depending on file mode and still keep behavior the same, you could let the image’s Python run it explicitly:

-RUN --mount=type=bind,ro,source=.,target=/repo /repo/docs/web/update-docs.py --repository-path /repo --output-content-path /src/content
+RUN --mount=type=bind,ro,source=.,target=/repo python /repo/docs/web/update-docs.py --repository-path /repo --output-content-path /src/content

Not strictly required, but often a bit more resilient across environments.

docs/web/export-services.py (1)

25-33: Stdout/file export behavior looks good; a couple of small polish opportunities

The new “file or stdout” behavior and CLI wiring look solid. Two small tweaks to consider:

  1. Make the output_file check explicit to avoid edge cases and improve readability:
-    # Write to YAML file or stdout
-    if output_file:
+    # Write to YAML file or stdout
+    if output_file is not None:
  1. Update the --output-file help text to reflect the new default-to-stdout behavior, e.g.:
-    parser.add_argument(
-        "--output-file",
-        type=str,
-        help="Specify the output YAML file path"
-    )
+    parser.add_argument(
+        "--output-file",
+        type=str,
+        help="Specify the output YAML file path (defaults to stdout if omitted)"
+    )

These don’t change behavior but make the intent clearer to callers.

Also applies to: 71-84, 100-103, 115-116

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8ec3833 and 36655fe.

📒 Files selected for processing (8)
  • docs/web/.gitignore (1 hunks)
  • docs/web/Dockerfile (1 hunks)
  • docs/web/README.md (1 hunks)
  • docs/web/__init__.py (0 hunks)
  • docs/web/docker_scanner.py (1 hunks)
  • docs/web/export-services.py (4 hunks)
  • docs/web/update-docs.py (1 hunks)
  • scripts/infra-mcp/tools/get_container_tags.py (5 hunks)
💤 Files with no reviewable changes (1)
  • docs/web/init.py
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Python files must pass Ruff linting

Files:

  • docs/web/export-services.py
  • docs/web/update-docs.py
  • docs/web/docker_scanner.py
  • scripts/infra-mcp/tools/get_container_tags.py
**/Dockerfile*

📄 CodeRabbit inference engine (AGENTS.md)

Dockerfiles must pass Hadolint linting

Files:

  • docs/web/Dockerfile
🧬 Code graph analysis (4)
docs/web/export-services.py (2)
docs/web/docker_scanner.py (1)
  • DockerComposeScanner (10-96)
docs/web/export_services.py (1)
  • export_services (26-77)
docs/web/update-docs.py (3)
docs/web/docker_scanner.py (1)
  • DockerComposeScanner (10-96)
docs/web/git_utils.py (1)
  • get_git_root (20-45)
docs/web/link_processor.py (1)
  • LinkProcessor (8-153)
docs/web/docker_scanner.py (1)
docs/web/compose_processor.py (1)
  • ComposeFileProcessor (8-112)
docs/web/Dockerfile (1)
docs/web/update_docs.py (4)
  • process (197-207)
  • process_docker_directory (136-195)
  • process_directory (119-125)
  • __init__ (20-56)
🪛 Ruff (0.14.6)
scripts/infra-mcp/tools/get_container_tags.py

144-144: Unused method argument: limit

(ARG002)


216-216: Unused method argument: limit

(ARG002)

🔇 Additional comments (5)
scripts/infra-mcp/tools/get_container_tags.py (4)

38-86: LGTM! Robust version parsing with comprehensive edge case handling.

The method correctly handles various tag formats (v-prefixed, suffixed, numeric-only) and includes appropriate boundary checks and error handling.


124-142: LGTM! Clean implementation of flexible sorting.

The in-place sorting with support for multiple sort orders is well-structured and easy to understand.


421-433: LGTM! Proper propagation of sort parameter.

The sort_by parameter is correctly extracted from arguments with a sensible default and properly passed to both registry methods.


586-587: Breaking change in default sort behavior: users expecting registry order will now get version-sorted results.

The default sort order changed from 'default' (registry order) to 'version'. This is a breaking change for existing scripts or workflows that depend on this tool, though no internal usages are found in the repository. Users can preserve the old behavior by explicitly passing --sort default.

Since this is a newly added/recently modified standalone CLI tool (commit "36655fe Implement tag sorting by version") with no references in the codebase, the impact is limited to external users and manual invocations. The change is well-intentioned (version sorting is more useful by default), but users should be aware of this shift in default behavior.

docs/web/README.md (1)

8-8: Build-process doc now matches the new script entrypoint

The reference to update-docs.py here aligns with the Dockerfile and .gitignore updates; the build description looks consistent.

@bubacoder
bubacoder force-pushed the feature/helper-scripts branch from 36655fe to 0aa06d6 Compare December 1, 2025 09:46

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
scripts/infra-mcp/tools/get_container_tags.py (1)

107-141: Architecture-aware digest extraction and tag normalization look good; minor reuse opportunity

The _extract_arch_digest + _create_tag_data_dict helpers correctly standardize Docker Hub tag data and pick the digest for the requested OS/arch pair, which should make downstream consumers much simpler.

For consistency and to avoid drift, you could consider reusing _parse_arch similarly in the registry manifest loop (where architecture.split('/') is duplicated) so both Docker Hub and registry paths share the same parsing semantics, but that’s optional.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 36655fe and 0aa06d6.

📒 Files selected for processing (9)
  • docs/web/.gitignore (1 hunks)
  • docs/web/Dockerfile (1 hunks)
  • docs/web/README.md (1 hunks)
  • docs/web/Taskfile.web.yaml (1 hunks)
  • docs/web/__init__.py (0 hunks)
  • docs/web/docker_scanner.py (1 hunks)
  • docs/web/export-services.py (5 hunks)
  • docs/web/update-docs.py (1 hunks)
  • scripts/infra-mcp/tools/get_container_tags.py (6 hunks)
💤 Files with no reviewable changes (1)
  • docs/web/init.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • docs/web/Dockerfile
  • docs/web/.gitignore
  • docs/web/README.md
  • docs/web/export-services.py
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

YAML files must pass YAML linting and validation

Files:

  • docs/web/Taskfile.web.yaml
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Python files must pass Ruff linting

Files:

  • docs/web/docker_scanner.py
  • docs/web/update-docs.py
  • scripts/infra-mcp/tools/get_container_tags.py
🧬 Code graph analysis (2)
docs/web/docker_scanner.py (1)
docs/web/compose_processor.py (1)
  • ComposeFileProcessor (8-112)
docs/web/update-docs.py (3)
docs/web/docker_scanner.py (1)
  • DockerComposeScanner (10-96)
docs/web/git_utils.py (1)
  • get_git_root (20-45)
docs/web/link_processor.py (1)
  • LinkProcessor (8-153)
🪛 Ruff (0.14.6)
scripts/infra-mcp/tools/get_container_tags.py

190-190: Unused method argument: limit

(ARG002)


236-236: Unused method argument: limit

(ARG002)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: check
🔇 Additional comments (9)
docs/web/update-docs.py (1)

11-13: LGTM! Import changes align with the script standardization.

The conversion to absolute imports is consistent with the broader PR changes and aligns with the script's standalone execution model (indicated by the shebang on line 1).

docs/web/Taskfile.web.yaml (1)

57-60: LGTM! New task follows the existing pattern.

The new task correctly invokes the export-services.py script with the appropriate output file path. The script is executable with proper shebang, and the output directory exists. YAML file passes linting validation.

docs/web/docker_scanner.py (1)

7-7: Verify the import works with the new execution context.

The absolute import from compose_processor import ComposeFileProcessor assumes docs/web is in PYTHONPATH or the script is executed from that directory. Ensure this works correctly in all execution contexts (local development, Docker container, CI/CD) by either:

  • Documenting the required execution context, or
  • Adjusting the import to use relative imports or explicit path configuration (e.g., using __file__ or sys.path manipulation)
scripts/infra-mcp/tools/get_container_tags.py (6)

38-87: Version parsing helper matches documented tag formats

The normalization and digit-only extraction here align with the documented examples (18.1, v18.1, 18.1-trixie, etc.), and the early exits keep non-version tags out of version-aware sorting without raising. This looks solid for Docker-style tags.


142-174: Version-aware sort key correctly enforces the intended priority

The key structure (latest > versioned tags > non-version tags) and padding of the version tuple to fixed length match the docstring and work cleanly with reverse=True in _sort_tags, giving you descending version order while always pinning latest to the top. No issues here.


175-189: _sort_tags is a clear, extensible dispatcher for sort modes

The three-mode dispatcher (version, updated, default) is straightforward, and delegating to _version_sort_key centralizes the more complex logic nicely. Once _parse_datetime handles HTTP dates properly, this should give predictable behavior across Docker Hub and registries.


441-454: sort_by propagation into get_image_tags is good; be aware of behavior change for other subcommands

Deriving sort_by via getattr(args, 'sort', 'version') and passing it through to get_docker_hub_tags/get_registry_tags wires the CLI option through cleanly.

Note that for list-same-hash and get-most-specific-tag, there is no --sort flag, so these flows now implicitly use "version" sorting instead of whatever the registry’s default order was. Because _version_sort_key prioritizes latest and then highest versions, the default “reference tag” when args.tag is omitted will effectively be latest or the highest version, which is probably what you want—but it is a subtle behavior change worth double-checking against existing usage.


606-607: CLI --sort option for list-recent is well-scoped and consistent with backend

Exposing --sort with choices ['version', 'updated', 'default'] and defaulting to 'version' matches the helper behavior and keeps invalid values out at parse time. This is a nice, minimal CLI surface expansion.


190-201: The limit parameter in get_docker_hub_tags IS actually used and does not violate Ruff ARG002

The parameter is used at line 231 with return tag_data[:limit], which slices the result list to the specified limit. The code is already correctly implementing the suggested fix. No changes are required, and the current implementation passes Ruff linting.

The docstring note "(unused in fetching, used by caller)" is misleading since limit is actively used in the method body to trim results. Consider clarifying the docstring to say something like "Maximum number of tags to return (used to limit results)".

Likely an incorrect or invalid review comment.

Comment thread scripts/infra-mcp/tools/get_container_tags.py
Comment thread scripts/infra-mcp/tools/get_container_tags.py
@bubacoder
bubacoder merged commit 2c1af56 into main Dec 1, 2025
4 checks passed
@bubacoder
bubacoder deleted the feature/helper-scripts branch December 1, 2025 10:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant