diff --git a/.github/workflows/build_docs.yml b/.github/workflows/build_docs.yml new file mode 100644 index 0000000..8fda133 --- /dev/null +++ b/.github/workflows/build_docs.yml @@ -0,0 +1,49 @@ +name: Build docs + +on: + push: + branches: + - main + tags: + - '*' + pull_request: + branches: + - main + workflow_dispatch: # Manual trigger for publishing docs + +jobs: + build-docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: 3.9 + + - uses: actions/cache@v5 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/pyproject.toml') }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: Install dependencies + run: | + pip install --upgrade pip + pip install setuptools wheel + pip install -e .[extra] --group docs + python -c 'import cottoncandy; print(cottoncandy.__version__)' # create config + + - name: Build documents + run: | + cd gendoc && make githubio-docs && cd .. + touch docs/.nojekyll + + - name: Publish to gh-pages if tagged + if: startsWith(github.ref, 'refs/tags') + uses: JamesIves/github-pages-deploy-action@v4.8.0 + with: + branch: gh-pages + folder: docs diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 587d1dd..39e5987 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -27,7 +27,7 @@ jobs: - uses: actions/cache@v5 with: path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/setup.py') }} + key: ${{ runner.os }}-pip-${{ hashFiles('**/pyproject.toml') }} restore-keys: | ${{ runner.os }}-pip- diff --git a/gendoc/remove_keys.py b/gendoc/remove_keys.py index 0a93197..3023806 100644 --- a/gendoc/remove_keys.py +++ b/gendoc/remove_keys.py @@ -1,4 +1,5 @@ import os +import re import sys from cottoncandy import options @@ -6,6 +7,10 @@ args = sys.argv paths_to_search_list = args[1:] +if len(paths_to_search_list) == 0: + print("No paths provided; nothing to sanitize.") + sys.exit(1) + replacement_dict = { options.config.get('login', 'access_key'): "FAKE_ACCESS_KEY", options.config.get('login', 'secret_key'): "FAKE_SECRET_KEY", @@ -13,14 +18,59 @@ options.config.get('basic', 'default_bucket'): "FAKE_DEFAULT_BUCKET", } -max_depth = 4 -intermediate_path = "" -for depth in range(max_depth): - intermediate_path = os.path.join(intermediate_path, "**") - for path_to_search in paths_to_search_list: - "Searching files in {path_to_search}".format(path_to_search=path_to_search) - for suffix in ["html", "js", "txt"]: - for word, replacement in replacement_dict.items(): - cmd = "rpl -iR {word} {replacement} {path_to_search}".format(word=word, replacement=replacement, path_to_search=os.path.join(path_to_search, intermediate_path, "*.{suffix}".format(suffix=suffix))) - print(cmd) - print(os.system(cmd)) +suffixes = {".html", ".js", ".txt"} + +files_sanitized = 0 +files_changed = 0 +errors = [] + +def should_replace(word): + return isinstance(word, str) and len(word) > 0 + +def sanitize_file(file_path, replacements): + global files_sanitized, files_changed + try: + with open(file_path, "r", encoding="utf-8", errors="replace") as handle: + content = handle.read() + except OSError: + errors.append(file_path) + return + + original = content + for word, replacement in replacements.items(): + if not should_replace(word): + continue + content = re.sub(re.escape(word), replacement, content, flags=re.IGNORECASE) + + if content == original: + files_sanitized += 1 + return + + try: + with open(file_path, "w", encoding="utf-8", errors="replace") as handle: + handle.write(content) + files_sanitized += 1 + files_changed += 1 + except OSError: + errors.append(file_path) + +for path_to_search in paths_to_search_list: + if not os.path.exists(path_to_search): + errors.append(path_to_search) + continue + + for root, _, files in os.walk(path_to_search): + for filename in files: + if os.path.splitext(filename)[1] not in suffixes: + continue + sanitize_file(os.path.join(root, filename), replacement_dict) + +print("Sanitized {count} files; updated {changed} files; {errors} errors.".format( + count=files_sanitized, + changed=files_changed, + errors=len(errors), +)) + +if len(errors) > 0: + print("Sanitization failed; see file access errors in logs.") + sys.exit(1) diff --git a/gendoc/tools/build_modref_templates.py b/gendoc/tools/build_modref_templates.py index 6ad30d6..c24bb69 100644 --- a/gendoc/tools/build_modref_templates.py +++ b/gendoc/tools/build_modref_templates.py @@ -11,7 +11,7 @@ from apigen import ApiDocWriter # version comparison -from distutils.version import LooseVersion as V +from looseversion import LooseVersion as V # ***************************************************************************** diff --git a/pyproject.toml b/pyproject.toml index 973df8b..1d14eae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,7 +43,10 @@ test = [ "pytest-rerunfailures", ] docs = [ + "looseversion", + "numpydoc", "sphinx", + "sphinx_bootstrap_theme", "sphinx-rtd-theme", ] dev = [