diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
deleted file mode 100644
index 091ed242..00000000
--- a/.github/workflows/docs.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-name: Update Sphinx documentation
-
-on: [push, pull_request, workflow_dispatch]
-
-permissions:
- contents: write
-
-jobs:
- docs:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- - uses: ./.github/actions/setup
- - run: make docs
- - name: Deploy to GitHub Pages
- uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0
- if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
- with:
- publish_branch: gh-pages
- github_token: ${{ secrets.GITHUB_TOKEN }}
- publish_dir: _build
- force_orphan: true
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 0cd297a0..2537a754 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -8,7 +8,29 @@ env:
GIT_CLIFF_VERSION: 2.12.0
jobs:
+ docs:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ - uses: gardenlinux/python-gardenlinux-lib/.github/actions/setup@main
+ - run: make docs
+ - if: ${{ github.ref_name != 'main' }}
+ name: Upload as artifact
+ uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # pin@v6.0.0
+ with:
+ name: docs
+ path: _build/
+ - if: ${{ github.ref_name == 'main' }}
+ name: Deploy to GitHub Pages
+ uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0
+ with:
+ publish_branch: gh-pages
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ publish_dir: _build
+ force_orphan: true
+
create-pre-release:
+ if: ${{ github.ref_name == 'main' }}
runs-on: ubuntu-latest
permissions:
contents: write
diff --git a/docs/_static/custom.css b/docs/_static/custom.css
new file mode 100644
index 00000000..91fdf275
--- /dev/null
+++ b/docs/_static/custom.css
@@ -0,0 +1,318 @@
+/* Garden Linux Design System - Sphinx Theme Customization */
+
+:root {
+ /* Primary Green */
+ --gardenlinux-primary-base: #027154;
+ --gardenlinux-primary-dark: #015a43;
+ --gardenlinux-primary-darker: #014332;
+ --gardenlinux-primary-darkest: #012c21;
+ --gardenlinux-primary-light: #038865;
+ --gardenlinux-primary-lighter: #04a076;
+ --gardenlinux-primary-lightest: #05b887;
+
+ /* Secondary Green */
+ --gardenlinux-secondary-base: #009f76;
+ --gardenlinux-secondary-dark: #008060;
+ --gardenlinux-secondary-darker: #006149;
+ --gardenlinux-secondary-darkest: #004232;
+ --gardenlinux-secondary-light: #00be8c;
+ --gardenlinux-secondary-lighter: #00dd9f;
+ --gardenlinux-secondary-lightest: #00fcb2;
+
+ /* Accent Yellow/Gold */
+ --gardenlinux-accent-base: #dfbe00;
+ --gardenlinux-accent-dark: #b29900;
+ --gardenlinux-accent-darker: #857400;
+ --gardenlinux-accent-darkest: #584f00;
+ --gardenlinux-accent-light: #ffd500;
+ --gardenlinux-accent-lighter: #ffec33;
+ --gardenlinux-accent-lightest: #fff366;
+
+ /* Semantic Colors */
+ --gardenlinux-success: #009f76;
+ --gardenlinux-success-light: #00be8c;
+ --gardenlinux-success-dark: #008060;
+ --gardenlinux-warning: #dfbe00;
+ --gardenlinux-warning-light: #ffd500;
+ --gardenlinux-warning-dark: #b29900;
+ --gardenlinux-error: #d32f2f;
+ --gardenlinux-error-light: #ef5350;
+ --gardenlinux-error-dark: #c62828;
+ --gardenlinux-info: #027154;
+ --gardenlinux-info-light: #038865;
+ --gardenlinux-info-dark: #015a43;
+}
+
+/* Override Read the Docs theme colors with Garden Linux colors */
+.wy-side-nav-search {
+ background-color: var(--gardenlinux-primary-base);
+}
+
+.wy-side-nav-search > a {
+ color: #ffffff !important;
+}
+
+.wy-side-nav-search > div.version {
+ color: rgba(255, 255, 255, 0.9) !important;
+}
+
+/* Ensure all text in the search header is white */
+.wy-side-nav-search * {
+ color: #ffffff !important;
+}
+
+.wy-side-nav-search input[type="text"] {
+ background-color: rgba(255, 255, 255, 0.2);
+ border-color: rgba(255, 255, 255, 0.3);
+ color: #ffffff;
+}
+
+.wy-side-nav-search input[type="text"]::placeholder {
+ color: rgba(255, 255, 255, 0.7);
+}
+
+/* Menu colors - improved readability - white background */
+.wy-side-scroll {
+ background-color: #ffffff;
+}
+
+.wy-menu-vertical {
+ background-color: #ffffff;
+}
+
+.wy-menu-vertical a {
+ color: #1a1a1a !important;
+ font-weight: 500;
+}
+
+/* Make unselected/collapsed menu items more readable */
+.wy-menu-vertical li:not(.current) > a {
+ color: #1a1a1a !important;
+}
+
+.wy-menu-vertical li.toctree-l1:not(.current) > a {
+ color: #1a1a1a !important;
+ font-weight: 500;
+}
+
+.wy-menu-vertical a:hover {
+ background-color: var(--gardenlinux-primary-lightest);
+ color: var(--gardenlinux-primary-darkest) !important;
+}
+
+.wy-menu-vertical li.current {
+ background-color: var(--gardenlinux-primary-lightest);
+}
+
+.wy-menu-vertical li.current > a {
+ color: var(--gardenlinux-primary-darkest) !important;
+ border-right: 3px solid var(--gardenlinux-primary-base);
+ font-weight: 600;
+ background-color: var(--gardenlinux-primary-lightest);
+}
+
+.wy-menu-vertical li.toctree-l1.current > a {
+ border-bottom: 1px solid var(--gardenlinux-primary-light);
+}
+
+/* Improve nested menu items readability */
+.wy-menu-vertical li.toctree-l2 a,
+.wy-menu-vertical li.toctree-l3 a,
+.wy-menu-vertical li.toctree-l4 a {
+ color: #1a1a1a !important;
+}
+
+/* Make unselected nested items more readable */
+.wy-menu-vertical li.toctree-l2:not(.current) > a,
+.wy-menu-vertical li.toctree-l3:not(.current) > a,
+.wy-menu-vertical li.toctree-l4:not(.current) > a {
+ color: #1a1a1a !important;
+ font-weight: 400;
+}
+
+.wy-menu-vertical li.toctree-l2.current > a,
+.wy-menu-vertical li.toctree-l3.current > a,
+.wy-menu-vertical li.toctree-l4.current > a {
+ background-color: var(--gardenlinux-primary-lightest) !important;
+ color: var(--gardenlinux-primary-darkest) !important;
+ font-weight: 600;
+}
+
+.wy-menu-vertical header,
+.wy-menu-vertical p.caption {
+ color: #1a1a1a;
+ font-weight: 600;
+ background-color: #ffffff;
+}
+
+/* Ensure all sidebar elements have white background */
+.wy-nav-side {
+ background-color: #ffffff !important;
+}
+
+.wy-nav-side .wy-menu-vertical {
+ background-color: #ffffff !important;
+}
+
+.wy-side-nav-search input[type="text"] {
+ background-color: rgba(255, 255, 255, 0.2);
+ border-color: rgba(255, 255, 255, 0.3);
+ color: #ffffff;
+}
+
+/* Override any black/dark backgrounds in sidebar */
+.wy-menu-vertical ul {
+ background-color: #ffffff !important;
+}
+
+.wy-menu-vertical li {
+ background-color: transparent;
+}
+
+/* Fix selected sub-sections - use light background with dark text */
+.wy-menu-vertical li.current a,
+.wy-menu-vertical li.current a:visited {
+ color: var(--gardenlinux-primary-darkest) !important;
+ background-color: var(--gardenlinux-primary-lightest) !important;
+}
+
+.wy-menu-vertical li.current a:hover {
+ color: var(--gardenlinux-primary-darkest) !important;
+ background-color: var(--gardenlinux-primary-lighter) !important;
+}
+
+/* Fix expand/collapse icon squares - make them dark and readable */
+.wy-menu-vertical .toctree-expand,
+.wy-menu-vertical span.toctree-expand,
+.wy-menu-vertical a .toctree-expand {
+ color: #1a1a1a !important;
+ border-color: #1a1a1a !important;
+ opacity: 1 !important;
+ font-weight: bold;
+}
+
+.wy-menu-vertical .toctree-expand:before,
+.wy-menu-vertical span.toctree-expand:before,
+.wy-menu-vertical a .toctree-expand:before {
+ color: #1a1a1a !important;
+ border-color: #1a1a1a !important;
+}
+
+/* Fix icon boxes for expandable items at all levels */
+.wy-menu-vertical li.toctree-l1 > a .toctree-expand,
+.wy-menu-vertical li.toctree-l2 > a .toctree-expand,
+.wy-menu-vertical li.toctree-l3 > a .toctree-expand,
+.wy-menu-vertical li.toctree-l4 > a .toctree-expand {
+ color: #1a1a1a !important;
+ border-color: #1a1a1a !important;
+ background-color: transparent !important;
+}
+
+/* Target the icon element directly */
+.wy-menu-vertical li .toctree-expand {
+ color: #1a1a1a !important;
+ border: 1px solid #1a1a1a !important;
+}
+
+/* Make sure plus/minus signs are dark */
+.wy-menu-vertical .toctree-expand:after {
+ color: #1a1a1a !important;
+}
+
+/* Links */
+a {
+ color: var(--gardenlinux-primary-base);
+}
+
+a:hover {
+ color: var(--gardenlinux-secondary-base);
+}
+
+a:visited {
+ color: var(--gardenlinux-primary-dark);
+}
+
+/* Code blocks */
+.highlight {
+ background-color: #f5f5f5;
+}
+
+.highlight .hll {
+ background-color: var(--gardenlinux-primary-lightest);
+}
+
+/* Buttons and important elements */
+.btn-primary,
+.rst-content .btn-primary {
+ background-color: var(--gardenlinux-primary-base);
+ border-color: var(--gardenlinux-primary-base);
+ color: #ffffff;
+}
+
+.btn-primary:hover,
+.rst-content .btn-primary:hover {
+ background-color: var(--gardenlinux-primary-dark);
+ border-color: var(--gardenlinux-primary-dark);
+}
+
+/* Admonitions */
+.rst-content .admonition {
+ border-left: 4px solid;
+}
+
+.rst-content .admonition.note {
+ border-left-color: var(--gardenlinux-info);
+ background-color: var(--gardenlinux-info-lightest);
+}
+
+.rst-content .admonition.warning {
+ border-left-color: var(--gardenlinux-warning);
+ background-color: var(--gardenlinux-accent-lightest);
+}
+
+.rst-content .admonition.danger,
+.rst-content .admonition.error {
+ border-left-color: var(--gardenlinux-error);
+ background-color: var(--gardenlinux-error-light);
+}
+
+.rst-content .admonition.success,
+.rst-content .admonition.tip {
+ border-left-color: var(--gardenlinux-success);
+ background-color: var(--gardenlinux-secondary-lightest);
+}
+
+/* Headers */
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ color: var(--gardenlinux-primary-base);
+}
+
+/* Table of contents */
+.rst-content .toctree-wrapper > p.caption {
+ color: var(--gardenlinux-primary-base);
+ font-weight: bold;
+}
+
+/* Logo styling */
+.logo {
+ max-width: 250px;
+ height: auto;
+ margin: 10px 0;
+}
+
+/* Footer */
+footer {
+ border-top: 1px solid var(--gardenlinux-primary-light);
+}
+
+/* Responsive adjustments */
+@media screen and (max-width: 768px) {
+ .wy-side-nav-search {
+ padding: 0.809em;
+ }
+}
diff --git a/docs/_static/gardenlinux-logo.svg b/docs/_static/gardenlinux-logo.svg
new file mode 100644
index 00000000..4b63366a
--- /dev/null
+++ b/docs/_static/gardenlinux-logo.svg
@@ -0,0 +1 @@
+
diff --git a/docs/api.rst b/docs/api.rst
new file mode 100644
index 00000000..2d68330a
--- /dev/null
+++ b/docs/api.rst
@@ -0,0 +1,22 @@
+API Reference
+=============
+
+This section provides detailed documentation for all Python modules and classes in python-gardenlinux-lib.
+
+.. automodule:: gardenlinux
+ :members:
+
+.. automodule:: gardenlinux.apt
+ :members:
+
+.. automodule:: gardenlinux.features
+ :members:
+
+.. automodule:: gardenlinux.flavors
+ :members:
+
+.. automodule:: gardenlinux.git
+ :members:
+
+.. automodule:: gardenlinux.oci
+ :members:
diff --git a/docs/cli.rst b/docs/cli.rst
new file mode 100644
index 00000000..80cc963c
--- /dev/null
+++ b/docs/cli.rst
@@ -0,0 +1,63 @@
+Command-Line Interface
+======================
+
+This page documents all available command-line tools provided by python-gardenlinux-lib.
+
+Features Commands
+-----------------
+
+gl-cname
+~~~~~~~~
+
+Generate a canonical name (cname) from feature sets.
+
+.. autoprogram:: gardenlinux.features.cname_main:get_parser()
+
+gl-features-parse
+~~~~~~~~~~~~~~~~~
+
+Parse and extract information from GardenLinux features.
+
+.. autoprogram:: gardenlinux.features.__main__:get_parser()
+
+Flavors Commands
+----------------
+
+gl-flavors-parse
+~~~~~~~~~~~~~~~~
+
+Parse flavors.yaml and generate combinations.
+
+.. autoprogram:: gardenlinux.flavors.__main__:get_parser()
+
+OCI Commands
+------------
+
+gl-oci
+~~~~~~
+
+Push OCI artifacts to a registry and manage manifests.
+
+.. click:: gardenlinux.oci.__main__:cli
+ :prog: gl-oci
+ :show-nested:
+
+S3 Commands
+-----------
+
+gl-s3
+~~~~~
+
+Upload and download artifacts from S3 buckets.
+
+.. autoprogram:: gardenlinux.s3.__main__:get_parser()
+
+GitHub Commands
+---------------
+
+gl-gh-release
+~~~~~~~~~~~~~~
+
+Create and manage GitHub releases.
+
+.. autoprogram:: gardenlinux.github.release.__main__:get_parser()
diff --git a/docs/conf.py b/docs/conf.py
index 0464e350..30c97c2f 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -1,8 +1,4 @@
-import os
-import sys
-
-sys.path.insert(0, os.path.abspath("../src"))
-
+from sphinx.application import Sphinx
# Configuration file for the Sphinx documentation builder.
#
@@ -12,9 +8,9 @@
# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
-project = "python-gardenlinux-lib"
-copyright = "2024, gardenlinux maintainer"
-author = "gardenlinux maintainer"
+project = "Garden Linux Python Library"
+copyright = "2024, Garden Linux Maintainers"
+author = "Garden Linux Maintainers"
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
@@ -24,6 +20,8 @@
"sphinx.ext.coverage",
"sphinx_rtd_theme",
"sphinx.ext.napoleon",
+ "sphinx_click",
+ "sphinxcontrib.autoprogram",
]
templates_path = ["_templates"]
@@ -36,3 +34,24 @@
html_theme = "sphinx_rtd_theme"
html_static_path = ["_static"]
+
+# Logo configuration
+html_logo = "_static/gardenlinux-logo.svg"
+
+html_theme_options = {
+ "logo_only": False,
+ "prev_next_buttons_location": "bottom",
+ "style_external_links": True,
+ "vcs_pageview_mode": "",
+ "style_nav_header_background": "#027154", # Primary green
+ "collapse_navigation": True,
+ "sticky_navigation": True,
+ "navigation_depth": 4,
+ "includehidden": True,
+ "titles_only": False,
+}
+
+
+# Add custom CSS
+def setup(app: Sphinx) -> None:
+ app.add_css_file("custom.css")
diff --git a/docs/index.rst b/docs/index.rst
index c6fa838c..9de35821 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -1,28 +1,63 @@
-.. parse_features_lib documentation master file, created by
- sphinx-quickstart on Thu Aug 15 22:42:36 2024.
- You can adapt this file completely to your liking, but it should at least
- contain the root `toctree` directive.
-
-python-gardenlinux-lib documentation
-====================================
-.. automodule:: gardenlinux
- :members:
-.. automodule:: gardenlinux.apt
- :members:
-.. automodule:: gardenlinux.features
- :members:
-.. automodule:: gardenlinux.flavors
- :members:
-.. automodule:: gardenlinux.git
- :members:
-.. automodule:: gardenlinux.oci
- :members:
+Garden Linux Python Library Documentation
+==========================================
+
+Welcome to the Garden Linux Python Library documentation. This library provides
+Python tools and utilities for working with Garden Linux features, flavors,
+OCI artifacts, S3 buckets, and GitHub releases.
+
+.. image:: _static/gardenlinux-logo.svg
+ :alt: Garden Linux Logo
+ :class: logo
+ :align: center
+
+Overview
+--------
+
+The Garden Linux Python Library is a comprehensive toolkit for managing and
+interacting with Garden Linux components. It includes:
+
+* **Feature Management**: Parse and work with Garden Linux features and generate canonical names
+* **Flavor Processing**: Parse flavors.yaml and generate combinations
+* **OCI Operations**: Push OCI artifacts to registries and manage manifests
+* **S3 Integration**: Upload and download artifacts from S3 buckets
+* **GitHub Integration**: Create and manage GitHub releases with release notes
+
+Quick Start
+-----------
+
+Command-Line Interface
+~~~~~~~~~~~~~~~~~~~~~~
+
+The library provides several command-line tools for common operations. See the
+:doc:`Command-Line Interface documentation ` for detailed information about
+all available commands.
+
+Release Management
+~~~~~~~~~~~~~~~~~~
+
+For information about versioning and release procedures, see the
+:doc:`Release documentation `.
+
+API Reference
+~~~~~~~~~~~~~
+
+For detailed Python API documentation, including all modules, classes, and
+functions, see the :doc:`API Reference `.
+
+Documentation Sections
+----------------------
+
.. toctree::
:maxdepth: 3
- :caption: Contents:
+ :caption: Documentation:
+
+ cli
+ release
+ api
+
+Additional Resources
+--------------------
-Indices and tables
-==================
-* :ref:`genindex`
-* :ref:`modindex`
-* :ref:`search`
+* :ref:`genindex` - Complete index of all functions, classes, and modules
+* :ref:`modindex` - Index of all modules
+* :ref:`search` - Search the documentation
diff --git a/docs/release.rst b/docs/release.rst
index da6a50a0..0b98175f 100644
--- a/docs/release.rst
+++ b/docs/release.rst
@@ -1,5 +1,5 @@
-python-gardenlinux-lib release documentation
-============================================
+release documentation
+=====================
*python-gardenlinux-lib* strictly follow syntax and intention of `Semantic Versioning `. Each release reflects the intention and expected impact therefore.
diff --git a/poetry.lock b/poetry.lock
index f82f3135..18bb2b95 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -821,7 +821,7 @@ version = "8.3.1"
description = "Composable command line interface toolkit"
optional = false
python-versions = ">=3.10"
-groups = ["main"]
+groups = ["main", "docs"]
files = [
{file = "click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6"},
{file = "click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a"},
@@ -841,7 +841,7 @@ files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
-markers = {main = "platform_system == \"Windows\"", dev = "platform_system == \"Windows\" or sys_platform == \"win32\"", docs = "sys_platform == \"win32\""}
+markers = {main = "platform_system == \"Windows\"", dev = "platform_system == \"Windows\" or sys_platform == \"win32\"", docs = "platform_system == \"Windows\" or sys_platform == \"win32\""}
[[package]]
name = "coverage"
@@ -2287,6 +2287,23 @@ sphinxcontrib-jsmath = ">=1.0.1"
sphinxcontrib-qthelp = ">=1.0.6"
sphinxcontrib-serializinghtml = ">=1.1.9"
+[[package]]
+name = "sphinx-click"
+version = "4.4.0"
+description = "Sphinx extension that automatically documents click applications"
+optional = false
+python-versions = ">=3.7"
+groups = ["docs"]
+files = [
+ {file = "sphinx-click-4.4.0.tar.gz", hash = "sha256:cc67692bd28f482c7f01531c61b64e9d2f069bfcf3d24cbbb51d4a84a749fa48"},
+ {file = "sphinx_click-4.4.0-py3-none-any.whl", hash = "sha256:2821c10a68fc9ee6ce7c92fad26540d8d8c8f45e6d7258f0e4fb7529ae8fab49"},
+]
+
+[package.dependencies]
+click = ">=7.0"
+docutils = "*"
+sphinx = ">=2.0"
+
[[package]]
name = "sphinx-rtd-theme"
version = "3.1.0"
@@ -2324,6 +2341,21 @@ lint = ["mypy", "ruff (==0.5.5)", "types-docutils"]
standalone = ["Sphinx (>=5)"]
test = ["pytest"]
+[[package]]
+name = "sphinxcontrib-autoprogram"
+version = "0.1.9"
+description = "Documenting CLI programs"
+optional = false
+python-versions = ">=3.8"
+groups = ["docs"]
+files = [
+ {file = "sphinxcontrib-autoprogram-0.1.9.tar.gz", hash = "sha256:219655507fadca29b3062b5d86c37d94db48f03bde4b58d61526872bf72f57cc"},
+ {file = "sphinxcontrib_autoprogram-0.1.9-py2.py3-none-any.whl", hash = "sha256:79a5282d7640337e4bf11f624970a43709f1b704c5c59a59756d45e824db5301"},
+]
+
+[package.dependencies]
+Sphinx = ">=1.2"
+
[[package]]
name = "sphinxcontrib-devhelp"
version = "2.0.0"
@@ -2584,4 +2616,4 @@ test = ["pytest", "pytest-cov"]
[metadata]
lock-version = "2.1"
python-versions = ">=3.13,!=3.14.1"
-content-hash = "0e92dd06c0f4f5a06988e7218eaed5d650d9a5a24bb2c8e2c6b5cda303f53f07"
+content-hash = "d3d6d40261960ebf3ab3a7a3a939c9f61381a502ec3a33ffc680eef810db2116"
diff --git a/pyproject.toml b/pyproject.toml
index 18db1b22..bb939d71 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -39,6 +39,8 @@ boto3-stubs = { extras = ["s3"], version = "^1.42.30" }
[tool.poetry.group.docs.dependencies]
sphinx-rtd-theme = "^3.0.2"
+sphinx-click = "^4.4.0"
+sphinxcontrib-autoprogram = "^0.1.8"
[tool.poetry.scripts]
gl-cname = "gardenlinux.features.cname_main:main"
diff --git a/src/gardenlinux/features/__main__.py b/src/gardenlinux/features/__main__.py
index 76224da2..d0b7f8ad 100644
--- a/src/gardenlinux/features/__main__.py
+++ b/src/gardenlinux/features/__main__.py
@@ -39,33 +39,98 @@
"""
-def main() -> None:
+def get_parser() -> argparse.ArgumentParser:
"""
- gl-features-parse main()
+ Get the argument parser for gl-features-parse.
+ Used for documentation generation.
- :since: 0.7.0
+ :return: ArgumentParser instance
+ :since: 0.10.9
"""
- parser = argparse.ArgumentParser()
+ parser = argparse.ArgumentParser(
+ prog="gl-features-parse",
+ description="Parse and extract information from GardenLinux features.",
+ )
+
+ parser.add_argument(
+ "--arch",
+ dest="arch",
+ help="Target architecture (e.g., amd64, arm64). Overrides architecture from cname.",
+ )
+
+ parser.add_argument(
+ "--cname",
+ dest="cname",
+ required=True,
+ help="Canonical name (cname) to parse. Must be a valid GardenLinux canonical name.",
+ )
+
+ parser.add_argument(
+ "--commit",
+ dest="commit",
+ help="Git commit hash. If not specified, will be read from COMMIT file or release file.",
+ )
+
+ parser.add_argument(
+ "--feature-dir",
+ default="features",
+ help="Path to the features directory (default: 'features'). Either --feature-dir or --release-file must be provided.",
+ )
+
+ parser.add_argument(
+ "--release-file",
+ dest="release_file",
+ help="Path to a release file containing cname metadata. Either --feature-dir or --release-file must be provided.",
+ )
+
+ parser.add_argument(
+ "--default-arch",
+ dest="default_arch",
+ help="Default architecture to use if architecture cannot be determined from cname or other sources.",
+ )
+
+ parser.add_argument(
+ "--default-version",
+ dest="default_version",
+ help="Default version to use if version cannot be determined from files or other sources.",
+ )
- parser.add_argument("--arch", dest="arch")
- parser.add_argument("--cname", dest="cname", required=True)
- parser.add_argument("--commit", dest="commit")
- parser.add_argument("--feature-dir", default="features")
- parser.add_argument("--release-file", dest="release_file")
- parser.add_argument("--default-arch", dest="default_arch")
- parser.add_argument("--default-version", dest="default_version")
- parser.add_argument("--version", dest="version")
+ parser.add_argument(
+ "--version",
+ dest="version",
+ help="Version string. If not specified, will be read from VERSION file or release file.",
+ )
parser.add_argument(
"--ignore",
dest="ignore",
type=lambda arg: set([f for f in arg.split(",") if f]),
default=set(),
+ help="Comma-separated list of features to ignore when processing (e.g., 'feature1,feature2').",
)
- parser.add_argument("type", nargs="?", choices=_ARGS_TYPE_ALLOWED, default="cname")
+ parser.add_argument(
+ "type",
+ nargs="?",
+ choices=_ARGS_TYPE_ALLOWED,
+ default="cname",
+ help="Type of output to generate. Choices: {}. Default: 'cname'.".format(
+ ", ".join(_ARGS_TYPE_ALLOWED)
+ ),
+ )
+
+ return parser
+
+
+def main() -> None:
+ """
+ gl-features-parse main()
+
+ :since: 0.7.0
+ """
+ parser = get_parser()
args = parser.parse_args()
assert bool(args.feature_dir) or bool(args.release_file), (
diff --git a/src/gardenlinux/features/cname_main.py b/src/gardenlinux/features/cname_main.py
index 517d1cd0..5ce5998c 100644
--- a/src/gardenlinux/features/cname_main.py
+++ b/src/gardenlinux/features/cname_main.py
@@ -18,6 +18,52 @@
from .parser import Parser
+def get_parser() -> argparse.ArgumentParser:
+ """
+ Get the argument parser for gl-cname.
+ Used for documentation generation.
+
+ :return: ArgumentParser instance
+ :since: 1.0.0
+ """
+
+ parser = argparse.ArgumentParser(
+ prog="gl-cname",
+ description="Generate a canonical name (cname) from feature sets.",
+ )
+
+ parser.add_argument(
+ "--arch",
+ dest="arch",
+ help="Target architecture (e.g., amd64, arm64). If not specified, will be determined from the cname or feature set.",
+ )
+
+ parser.add_argument(
+ "--commit",
+ dest="commit",
+ help="Git commit hash. If not specified, will be read from COMMIT file in the GardenLinux root directory.",
+ )
+
+ parser.add_argument(
+ "--feature-dir",
+ default="features",
+ help="Path to the features directory (default: 'features').",
+ )
+
+ parser.add_argument(
+ "--version",
+ dest="version",
+ help="Version string. If not specified, will be read from VERSION file in the GardenLinux root directory.",
+ )
+
+ parser.add_argument(
+ "cname",
+ help="Canonical name (cname) to process. Must be a valid GardenLinux canonical name format.",
+ )
+
+ return parser
+
+
def main() -> None:
"""
gl-cname main()
@@ -25,14 +71,7 @@ def main() -> None:
:since: 0.7.0
"""
- parser = argparse.ArgumentParser()
-
- parser.add_argument("--arch", dest="arch")
- parser.add_argument("--commit", dest="commit")
- parser.add_argument("--feature-dir", default="features")
- parser.add_argument("--version", dest="version")
- parser.add_argument("cname")
-
+ parser = get_parser()
args = parser.parse_args()
re_match = re.match(
diff --git a/src/gardenlinux/flavors/__main__.py b/src/gardenlinux/flavors/__main__.py
index ea2974d3..9933298f 100644
--- a/src/gardenlinux/flavors/__main__.py
+++ b/src/gardenlinux/flavors/__main__.py
@@ -6,7 +6,7 @@
"""
import json
-from argparse import ArgumentParser, Namespace
+from argparse import ArgumentParser
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import Any, List, Tuple
@@ -47,12 +47,13 @@ def generate_markdown_table(combinations: List[Tuple[Any, str]]) -> str:
return table
-def parse_args() -> Namespace:
+def get_parser() -> ArgumentParser:
"""
- Parses arguments used for main()
+ Get the argument parser for gl-flavors-parse.
+ Used for documentation generation.
- :return: (object) Parsed argparse.ArgumentParser namespace
- :since: 0.7.0
+ :return: ArgumentParser instance
+ :since: 1.0.0
"""
parser = ArgumentParser(description="Parse flavors.yaml and generate combinations.")
@@ -62,67 +63,78 @@ def parse_args() -> Namespace:
default=None,
help="Commit hash to fetch flavors.yaml from GitHub. An existing 'flavors.yaml' file will be preferred.",
)
+
parser.add_argument(
"--no-arch",
action="store_true",
help="Exclude architecture from the flavor output.",
)
+
parser.add_argument(
"--include-only",
action="append",
default=[],
help="Restrict combinations to those matching wildcard patterns (can be specified multiple times).",
)
+
parser.add_argument(
"--exclude",
action="append",
default=[],
help="Exclude combinations based on wildcard patterns (can be specified multiple times).",
)
+
parser.add_argument(
"--build",
action="store_true",
help="Filter combinations to include only those with build enabled.",
)
+
parser.add_argument(
"--publish",
action="store_true",
help="Filter combinations to include only those with publish enabled.",
)
+
parser.add_argument(
"--test",
action="store_true",
help="Filter combinations to include only those with test enabled.",
)
+
parser.add_argument(
"--test-platform",
action="store_true",
help="Filter combinations to include only platforms with test-platform: true.",
)
+
parser.add_argument(
"--category",
action="append",
default=[],
help="Filter combinations to include only platforms belonging to the specified categories (can be specified multiple times).",
)
+
parser.add_argument(
"--exclude-category",
action="append",
default=[],
help="Exclude platforms belonging to the specified categories (can be specified multiple times).",
)
+
parser.add_argument(
"--json-by-arch",
action="store_true",
help="Output a JSON dictionary where keys are architectures and values are lists of flavors.",
)
+
parser.add_argument(
"--markdown-table-by-platform",
action="store_true",
help="Generate a markdown table by platform.",
)
- return parser.parse_args()
+ return parser
def main() -> None:
@@ -132,7 +144,8 @@ def main() -> None:
:since: 0.7.0
"""
- args = parse_args()
+ parser = get_parser()
+ args = parser.parse_args()
try:
flavors_data = _get_flavors_file_data(Path(Repository().root, "flavors.yaml"))
diff --git a/src/gardenlinux/github/release/__main__.py b/src/gardenlinux/github/release/__main__.py
index e403893f..013108b9 100644
--- a/src/gardenlinux/github/release/__main__.py
+++ b/src/gardenlinux/github/release/__main__.py
@@ -14,35 +14,146 @@
LOGGER = LoggerSetup.get_logger("gardenlinux.github", logging.INFO)
-def main() -> None:
- parser = argparse.ArgumentParser(description="GitHub Release Script")
- subparsers = parser.add_subparsers(dest="command")
-
- create_parser = subparsers.add_parser("create")
- create_parser.add_argument("--owner", default="gardenlinux")
- create_parser.add_argument("--repo", default="gardenlinux")
- create_parser.add_argument("--tag", required=True)
- create_parser.add_argument("--name")
- create_parser.add_argument("--body", required=True)
- create_parser.add_argument("--commit")
- create_parser.add_argument("--pre-release", action="store_true", default=True)
- create_parser.add_argument("--latest", action="store_true", default=False)
-
- create_parser_gl = subparsers.add_parser("create-with-gl-release-notes")
- create_parser_gl.add_argument("--owner", default="gardenlinux")
- create_parser_gl.add_argument("--repo", default="gardenlinux")
- create_parser_gl.add_argument("--tag", required=True)
- create_parser_gl.add_argument("--commit", required=True)
- create_parser_gl.add_argument("--latest", action="store_true", default=False)
- create_parser_gl.add_argument("--dry-run", action="store_true", default=False)
-
- upload_parser = subparsers.add_parser("upload")
- upload_parser.add_argument("--owner", default="gardenlinux")
- upload_parser.add_argument("--repo", default="gardenlinux")
- upload_parser.add_argument("--release_id", required=True)
- upload_parser.add_argument("--file_path", required=True)
- upload_parser.add_argument("--dry-run", action="store_true", default=False)
+def get_parser() -> argparse.ArgumentParser:
+ """
+ Get the argument parser for gl-gh-release.
+ Used for documentation generation.
+
+ :return: ArgumentParser instance
+ :since: 1.0.0
+ """
+
+ parser = argparse.ArgumentParser(
+ prog="gl-gh-release", description="Create and manage GitHub releases."
+ )
+ subparsers = parser.add_subparsers(dest="command", help="Available commands")
+
+ create_parser = subparsers.add_parser("create", help="Create a new GitHub release.")
+
+ create_parser.add_argument(
+ "--owner",
+ default="gardenlinux",
+ help="GitHub repository owner (default: 'gardenlinux').",
+ )
+
+ create_parser.add_argument(
+ "--repo",
+ default="gardenlinux",
+ help="GitHub repository name (default: 'gardenlinux').",
+ )
+
+ create_parser.add_argument(
+ "--tag", required=True, help="Git tag name for the release (required)."
+ )
+
+ create_parser.add_argument(
+ "--name", help="Release name/title. If not specified, the tag will be used."
+ )
+
+ create_parser.add_argument(
+ "--body", required=True, help="Release notes/description body (required)."
+ )
+
+ create_parser.add_argument(
+ "--commit",
+ help="Git commit hash. If not specified, the tag will be used to find the commit.",
+ )
+
+ create_parser.add_argument(
+ "--pre-release",
+ action="store_true",
+ default=True,
+ help="Mark the release as a pre-release (default: True).",
+ )
+
+ create_parser.add_argument(
+ "--latest",
+ action="store_true",
+ default=False,
+ help="Mark this release as the latest release (default: False).",
+ )
+
+ create_parser_gl = subparsers.add_parser(
+ "create-with-gl-release-notes",
+ help="Create a GitHub release with auto-generated GardenLinux release notes.",
+ )
+
+ create_parser_gl.add_argument(
+ "--owner",
+ default="gardenlinux",
+ help="GitHub repository owner (default: 'gardenlinux').",
+ )
+ create_parser_gl.add_argument(
+ "--repo",
+ default="gardenlinux",
+ help="GitHub repository name (default: 'gardenlinux').",
+ )
+
+ create_parser_gl.add_argument(
+ "--tag", required=True, help="Git tag name for the release (required)."
+ )
+
+ create_parser_gl.add_argument(
+ "--commit",
+ required=True,
+ help="Git commit hash used to generate release notes (required).",
+ )
+
+ create_parser_gl.add_argument(
+ "--latest",
+ action="store_true",
+ default=False,
+ help="Mark this release as the latest release (default: False).",
+ )
+
+ create_parser_gl.add_argument(
+ "--dry-run",
+ action="store_true",
+ default=False,
+ help="Perform a dry run without actually creating the release.",
+ )
+
+ upload_parser = subparsers.add_parser(
+ "upload", help="Upload a file to an existing GitHub release."
+ )
+
+ upload_parser.add_argument(
+ "--owner",
+ default="gardenlinux",
+ help="GitHub repository owner (default: 'gardenlinux').",
+ )
+
+ upload_parser.add_argument(
+ "--repo",
+ default="gardenlinux",
+ help="GitHub repository name (default: 'gardenlinux').",
+ )
+
+ upload_parser.add_argument(
+ "--release_id",
+ required=True,
+ help="GitHub release ID to upload the file to (required).",
+ )
+
+ upload_parser.add_argument(
+ "--file_path",
+ required=True,
+ help="Path to the file to upload (required).",
+ )
+
+ upload_parser.add_argument(
+ "--dry-run",
+ action="store_true",
+ default=False,
+ help="Perform a dry run without actually uploading the file.",
+ )
+
+ return parser
+
+
+def main() -> None:
+ parser = get_parser()
args = parser.parse_args()
if args.command == "create":
diff --git a/src/gardenlinux/s3/__main__.py b/src/gardenlinux/s3/__main__.py
index 1c2da70d..f794631c 100644
--- a/src/gardenlinux/s3/__main__.py
+++ b/src/gardenlinux/s3/__main__.py
@@ -10,27 +10,62 @@
from .s3_artifacts import S3Artifacts
-def main() -> None:
+def get_parser() -> argparse.ArgumentParser:
"""
- gl-s3 main()
+ Get the argument parser for gl-s3.
+ Used for documentation generation.
- :since: 0.8.0
+ :return: ArgumentParser instance
+ :since: 0.10.9
"""
- parser = argparse.ArgumentParser()
+ parser = argparse.ArgumentParser("gl-s3")
+
+ parser.add_argument(
+ "--bucket", dest="bucket", help="S3 bucket name to upload to or download from."
+ )
- parser.add_argument("--bucket", dest="bucket")
- parser.add_argument("--path", required=False, dest="path")
- parser.add_argument("--dry-run", action="store_true")
+ parser.add_argument(
+ "--path",
+ required=False,
+ dest="path",
+ help="Local directory path for upload (source) or download (destination).",
+ )
- subparsers = parser.add_subparsers(dest="action")
+ parser.add_argument(
+ "--dry-run",
+ action="store_true",
+ help="Perform a dry run without actually uploading or downloading files.",
+ )
+
+ subparsers = parser.add_subparsers(dest="action", help="Action to perform.")
download_parser = subparsers.add_parser("download-artifacts-from-bucket")
- download_parser.add_argument("--cname", required=False, dest="cname")
+
+ download_parser.add_argument(
+ "--cname",
+ required=False,
+ dest="cname",
+ help="Canonical name (cname) used as the S3 key prefix for artifacts.",
+ )
upload_parser = subparsers.add_parser("upload-artifacts-to-bucket")
- upload_parser.add_argument("--artifact-name", required=False, dest="artifact_name")
+ upload_parser.add_argument(
+ "--artifact-name", dest="artifact_name", help="S3 artifact base name."
+ )
+
+ return parser
+
+
+def main() -> None:
+ """
+ gl-s3 main()
+
+ :since: 0.8.0
+ """
+
+ parser = get_parser()
args = parser.parse_args()
if args.action == "download-artifacts-from-bucket":
diff --git a/tests/flavors/test_main.py b/tests/flavors/test_main.py
index b74a5e2b..8b0a0a89 100644
--- a/tests/flavors/test_main.py
+++ b/tests/flavors/test_main.py
@@ -44,7 +44,7 @@ def test_parse_args(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(sys, "argv", argv)
# Act
- args = fm.parse_args()
+ args = fm.get_parser().parse_args()
# Assert
assert args.no_arch is True