From 9f9028473dc28135413ca6e07a495f9313d33c52 Mon Sep 17 00:00:00 2001 From: Aditya Vaidya Date: Thu, 25 Sep 2025 01:00:05 -0700 Subject: [PATCH 1/9] Add missing dependencies for sphinx docs --- gendoc/tools/build_modref_templates.py | 2 +- pyproject.toml | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) 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 = [ From a7fd1de47df9df21c626c4f251ce5595547e36a9 Mon Sep 17 00:00:00 2001 From: Aditya Vaidya Date: Thu, 25 Sep 2025 01:08:20 -0700 Subject: [PATCH 2/9] CI add workflow to build docs --- .github/workflows/build_docs.yml | 50 ++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 .github/workflows/build_docs.yml diff --git a/.github/workflows/build_docs.yml b/.github/workflows/build_docs.yml new file mode 100644 index 0000000..2b57e8c --- /dev/null +++ b/.github/workflows/build_docs.yml @@ -0,0 +1,50 @@ +name: Build docs + +on: + push: + branches: + - main + tags: + - '*' + pull_request: + branches: + - main + +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@v4 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y rpl + 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 From e6809b8069cc93be81293dad137cc773953c6d94 Mon Sep 17 00:00:00 2001 From: Aditya Vaidya Date: Wed, 12 Nov 2025 20:28:59 -0800 Subject: [PATCH 3/9] CI fix cache key --- .github/workflows/build_docs.yml | 2 +- .github/workflows/run_tests.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_docs.yml b/.github/workflows/build_docs.yml index 2b57e8c..8e94aa7 100644 --- a/.github/workflows/build_docs.yml +++ b/.github/workflows/build_docs.yml @@ -24,7 +24,7 @@ jobs: - uses: actions/cache@v4 with: path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + key: ${{ runner.os }}-pip-${{ hashFiles('**/pyproject.toml') }} restore-keys: | ${{ runner.os }}-pip- 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- From 2c41ac1a079c27be24c4938d9850692070bcbee8 Mon Sep 17 00:00:00 2001 From: Aditya Vaidya Date: Thu, 25 Sep 2025 02:17:23 -0700 Subject: [PATCH 4/9] MNT convert key scrubber to Python to avoid leaking keys Also fixes other errors for the default config --- gendoc/remove_keys.py | 72 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 11 deletions(-) diff --git a/gendoc/remove_keys.py b/gendoc/remove_keys.py index 0a93197..3eb96e8 100644 --- a/gendoc/remove_keys.py +++ b/gendoc/remove_keys.py @@ -6,6 +6,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 +17,60 @@ 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): + if isinstance(word, bool) or (word is None) or (len(word) == 0): + return False + return isinstance(word, str) + +def sanitize_file(file_path, replacements): + global files_sanitized, files_changed + try: + with open(file_path, "r", encoding="utf-8", errors="ignore") 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 = content.replace(word, replacement) + + files_sanitized += 1 + if content == original: + return + + try: + with open(file_path, "w", encoding="utf-8", errors="ignore") as handle: + handle.write(content) + 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) From e76cda08769c305c5de891472a409ded454c4e0d Mon Sep 17 00:00:00 2001 From: Aditya Vaidya <648148+kroq-gar78@users.noreply.github.com> Date: Tue, 19 May 2026 17:23:43 -0700 Subject: [PATCH 5/9] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .github/workflows/build_docs.yml | 4 +--- gendoc/remove_keys.py | 7 ++++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build_docs.yml b/.github/workflows/build_docs.yml index 8e94aa7..3524bee 100644 --- a/.github/workflows/build_docs.yml +++ b/.github/workflows/build_docs.yml @@ -21,7 +21,7 @@ jobs: with: python-version: 3.9 - - uses: actions/cache@v4 + - uses: actions/cache@v5 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('**/pyproject.toml') }} @@ -30,8 +30,6 @@ jobs: - name: Install dependencies run: | - sudo apt-get update - sudo apt-get install -y rpl pip install --upgrade pip pip install setuptools wheel pip install -e .[extra] --group docs diff --git a/gendoc/remove_keys.py b/gendoc/remove_keys.py index 3eb96e8..ca3d032 100644 --- a/gendoc/remove_keys.py +++ b/gendoc/remove_keys.py @@ -24,9 +24,9 @@ errors = [] def should_replace(word): - if isinstance(word, bool) or (word is None) or (len(word) == 0): + if isinstance(word, bool) or (word is None) or (not isinstance(word, str)): return False - return isinstance(word, str) + return len(word) != 0 def sanitize_file(file_path, replacements): global files_sanitized, files_changed @@ -43,13 +43,14 @@ def sanitize_file(file_path, replacements): continue content = content.replace(word, replacement) - files_sanitized += 1 if content == original: + files_sanitized += 1 return try: with open(file_path, "w", encoding="utf-8", errors="ignore") as handle: handle.write(content) + files_sanitized += 1 files_changed += 1 except OSError: errors.append(file_path) From 15a6b52c8e85541c971b91f48fae534cc1c41e86 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 20 May 2026 01:20:40 +0000 Subject: [PATCH 6/9] Make docs key scrubbing case-insensitive Agent-Logs-Url: https://github.com/gallantlab/cottoncandy/sessions/9682fb4b-670f-4c5c-ad82-3ec96d1cdead Co-authored-by: kroq-gar78 <648148+kroq-gar78@users.noreply.github.com> --- gendoc/remove_keys.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gendoc/remove_keys.py b/gendoc/remove_keys.py index ca3d032..4cae729 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 @@ -41,7 +42,7 @@ def sanitize_file(file_path, replacements): for word, replacement in replacements.items(): if not should_replace(word): continue - content = content.replace(word, replacement) + content = re.sub(re.escape(word), replacement, content, flags=re.IGNORECASE) if content == original: files_sanitized += 1 From c87334d98ea478e3900e30d4e0e017c753ca876a Mon Sep 17 00:00:00 2001 From: Aditya Vaidya Date: Wed, 20 May 2026 01:19:26 -0700 Subject: [PATCH 7/9] 'replace' bad unicode characters instead of 'ignore' (per Gemini) --- gendoc/remove_keys.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gendoc/remove_keys.py b/gendoc/remove_keys.py index 4cae729..db97ff3 100644 --- a/gendoc/remove_keys.py +++ b/gendoc/remove_keys.py @@ -32,7 +32,7 @@ def should_replace(word): def sanitize_file(file_path, replacements): global files_sanitized, files_changed try: - with open(file_path, "r", encoding="utf-8", errors="ignore") as handle: + with open(file_path, "r", encoding="utf-8", errors="replace") as handle: content = handle.read() except OSError: errors.append(file_path) @@ -49,7 +49,7 @@ def sanitize_file(file_path, replacements): return try: - with open(file_path, "w", encoding="utf-8", errors="ignore") as handle: + with open(file_path, "w", encoding="utf-8", errors="replace") as handle: handle.write(content) files_sanitized += 1 files_changed += 1 From 914e19ff0cb33961f21d60ef652e11e022442d89 Mon Sep 17 00:00:00 2001 From: Aditya Vaidya Date: Wed, 20 May 2026 01:24:23 -0700 Subject: [PATCH 8/9] Simplify scrubbing check --- gendoc/remove_keys.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/gendoc/remove_keys.py b/gendoc/remove_keys.py index db97ff3..3023806 100644 --- a/gendoc/remove_keys.py +++ b/gendoc/remove_keys.py @@ -25,9 +25,7 @@ errors = [] def should_replace(word): - if isinstance(word, bool) or (word is None) or (not isinstance(word, str)): - return False - return len(word) != 0 + return isinstance(word, str) and len(word) > 0 def sanitize_file(file_path, replacements): global files_sanitized, files_changed From 583aa212458ea6ca8bbfd589c76ebdb96f33a7b1 Mon Sep 17 00:00:00 2001 From: Matteo Visconti di Oleggio Castello Date: Wed, 20 May 2026 15:37:49 -0700 Subject: [PATCH 9/9] Add workflow_dispatch for manual docs publishing Added manual trigger for publishing documentation. --- .github/workflows/build_docs.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build_docs.yml b/.github/workflows/build_docs.yml index 3524bee..8fda133 100644 --- a/.github/workflows/build_docs.yml +++ b/.github/workflows/build_docs.yml @@ -9,7 +9,8 @@ on: pull_request: branches: - main - + workflow_dispatch: # Manual trigger for publishing docs + jobs: build-docs: runs-on: ubuntu-latest