|
| 1 | +# Documentation Setup and Automation Guide |
| 2 | + |
| 3 | +This guide explains how to set up and maintain documentation for your Python library in a dedicated `docs` branch with GitHub Pages. It also details how all the steps, once configured, are automated via a GitHub Actions workflow that updates and builds documentation upon new releases. |
| 4 | + |
| 5 | +For example, for the repository **https://"https://github.com/ORGANIZATION/REPO/** the corresponding GitHub documentation page will be **https://"https://ORGANIZATION.github.io/REPO/**. |
| 6 | + |
| 7 | +Key points: |
| 8 | +- **Dedicated `docs` branch**: The documentation is served from the `docs` folder on the `docs` branch, utilizing GitHub Pages (so that it's updated automatically in the GitHub page when this folder is changed through a push). |
| 9 | +- **Automated `.rst` File Generation**: A Python script (`generate_doc_markdown.py`) scans your codebase and generates the `.rst` files automatically. |
| 10 | +- **Sphinx & Extensions**: Use Sphinx, `sphinx-automodapi`, and `pydata-sphinx-theme` to build the docs. |
| 11 | +- **CI/CD with GitHub Actions**: Once set up, the provided GitHub Actions workflow automatically updates the documentation every time a new release of the repository is published. |
| 12 | + |
| 13 | +Logical flow of documentation updates: |
| 14 | +1. **Initial Setup**: Configure `docs` branch, Sphinx, and dependencies. |
| 15 | +2. **Automated `.rst` Generation**: Use `generate_doc_markdown.py` to generate `.rst` files from code. |
| 16 | +3. **Building Locally (if needed)**: `make html` to verify documentation locally. |
| 17 | +4. **Release Trigger**: On publishing a new release, the GitHub Actions workflow: |
| 18 | + - Checks out `docs` branch and release code. |
| 19 | + - Generates `.rst` files. |
| 20 | + - Builds HTML docs. |
| 21 | + - Deploys the updated docs to `docs/latest` and `docs/<version>`. |
| 22 | +5. **Automatic Deployment**: Changes are committed back to `docs` branch and served on GitHub Pages. |
| 23 | + |
| 24 | +## 1. Create the `docs` Branch on GitHub |
| 25 | + |
| 26 | +- Create a `docs` branch in your repository. |
| 27 | +- Go to the repository’s **Settings**. |
| 28 | +- Under **Pages**, configure the site to be served from the `docs/` folder on the `docs` branch. |
| 29 | + |
| 30 | +## 2. Clean the `docs` Branch |
| 31 | + |
| 32 | +- In the `docs` branch, delete all files except for `.gitignore` (and of course `.git`). |
| 33 | +- The branch should now be nearly empty, ready for the documentation setup. |
| 34 | + |
| 35 | +## 3. Configuring Sphinx |
| 36 | + |
| 37 | +You need to perform the following steps checking out the `docs` branch of your repo. |
| 38 | + |
| 39 | +**Install Dependencies:** |
| 40 | + |
| 41 | +```bash |
| 42 | +pip install sphinx sphinx-automodapi pydata-sphinx-theme |
| 43 | +``` |
| 44 | + |
| 45 | +**Run `sphinx-quickstart`:** |
| 46 | + |
| 47 | +```bash |
| 48 | +sphinx-quickstart |
| 49 | +``` |
| 50 | + |
| 51 | +If the command is not found: |
| 52 | + |
| 53 | +```bash |
| 54 | +python -m sphinx.cmd.quickstart |
| 55 | +``` |
| 56 | + |
| 57 | +This generates: |
| 58 | + |
| 59 | +- `conf.py` (Sphinx configuration file) |
| 60 | +- `Makefile` |
| 61 | +- `index.rst` |
| 62 | +- `make.bat` |
| 63 | + |
| 64 | +**Edit `conf.py`:** |
| 65 | + |
| 66 | +- Add your code to `sys.path` so Sphinx can locate it: |
| 67 | + |
| 68 | + ```python |
| 69 | + import sys |
| 70 | + sys.path.insert(0, "release-code") |
| 71 | + ``` |
| 72 | + |
| 73 | +- Add code to get the release from enviroment variable: |
| 74 | + |
| 75 | + ```python |
| 76 | + import os |
| 77 | + |
| 78 | + # get release from environment variable |
| 79 | + version = os.environ.get("VERSION", "") |
| 80 | + if not version: |
| 81 | + print("Error: VERSION environment variable not set.") |
| 82 | + sys.exit(1) |
| 83 | + ``` |
| 84 | + |
| 85 | +- Edit/Add the version number to appear in the title: |
| 86 | + |
| 87 | + ```python |
| 88 | + release = version |
| 89 | + ``` |
| 90 | + |
| 91 | +- Add required extensions: |
| 92 | + |
| 93 | + ```python |
| 94 | + extensions = ["sphinx_automodapi.automodapi", "sphinx.ext.githubpages"] |
| 95 | + numpydoc_show_class_members = False |
| 96 | + automodapi_inheritance_diagram = False |
| 97 | + ``` |
| 98 | + |
| 99 | +- Set the theme and options (**change ORGANIZATION and REPO as needed**): |
| 100 | + |
| 101 | + ```python |
| 102 | + html_theme = "pydata_sphinx_theme" |
| 103 | + html_static_path = ["_static"] |
| 104 | + html_theme_options = { |
| 105 | + "switcher": { |
| 106 | + "json_url": "https://ORGANIZATION.github.io/REPO/latest/_static/switcher.json", |
| 107 | + "version_match": version, |
| 108 | + }, |
| 109 | + "navbar_end": [ |
| 110 | + "version-switcher", |
| 111 | + "navbar-icon-links", |
| 112 | + ], |
| 113 | + } |
| 114 | + ``` |
| 115 | + |
| 116 | +**Create/edit `index.rst`:** |
| 117 | + |
| 118 | +Place an `index.rst` file at the root of the `docs` branch: |
| 119 | + |
| 120 | +```rst |
| 121 | +.. yourproject documentation master file. |
| 122 | +
|
| 123 | +yourproject documentation |
| 124 | +========================= |
| 125 | +
|
| 126 | +.. toctree:: |
| 127 | + :maxdepth: 2 |
| 128 | + :caption: Contents: |
| 129 | +
|
| 130 | + src/Documentation |
| 131 | +``` |
| 132 | + |
| 133 | +## 4. Copy the File to Generate `.rst` Files with the Script |
| 134 | + |
| 135 | +Copy the Python script `generate_doc_markdown.py` to automate the `.rst` creation into the root folder of the `docs`branch. |
| 136 | + |
| 137 | +> This will automate the `.rst` creation. It: |
| 138 | +> |
| 139 | +> - Identifies top-level modules and packages in `release-code/<library_name>`. |
| 140 | +> - Generates `.rst` files for each module. |
| 141 | +> - Creates a `Documentation.rst` that acts as a table of contents. |
| 142 | +> |
| 143 | +> **Usage:** |
| 144 | +> |
| 145 | +> ```bash |
| 146 | +> python generate_doc_markdown.py <library_name> [--force] [--exclude=mod1,mod2,...] [--output-dir=src] |
| 147 | +> ``` |
| 148 | +> |
| 149 | +> **Arguments:** |
| 150 | +> |
| 151 | +> - `<library_name>`: The name of your library (the folder under `release-code`). |
| 152 | +> - `--force` / `-f`: Overwrite existing `.rst` files if present. |
| 153 | +> - `--exclude` / `-e`: Exclude specific submodules from documentation. |
| 154 | +> - `--output-dir` / `-o`: Directory to write output files, default is `src`. |
| 155 | +> |
| 156 | +> **Example:** |
| 157 | +> |
| 158 | +> ```bash |
| 159 | +> python generate_doc_markdown.py deeplay --force --exclude=_internal --output-dir=src |
| 160 | +> ``` |
| 161 | +> |
| 162 | +> This command creates the `src` folder (if needed), generates `.rst` files for each module, and updates `Documentation.rst` automatically. |
| 163 | +
|
| 164 | +## 5. Add the Version Switcher |
| 165 | +
|
| 166 | +Add the version switcher `switcher.json` to the folder `_static`of the `docs`branch. |
| 167 | +
|
| 168 | +``` |
| 169 | +[] |
| 170 | +``` |
| 171 | +
|
| 172 | +
|
| 173 | +> The version switcher allows users to navigate between different versions of your documentation directly from the site’s navigation bar. This is handled by the switcher.json file, stored in _static/switcher.json, which follows a structure like: |
| 174 | +> |
| 175 | +> ``` |
| 176 | +> [ |
| 177 | +> { |
| 178 | +> "name": "0.1.0", |
| 179 | +> "version": "0.1.0", |
| 180 | +> "url": "https://ORGANIZATION.github.io/REPO/0.1.0/" |
| 181 | +> } |
| 182 | +> ] |
| 183 | +> ``` |
| 184 | +> |
| 185 | +> How it works: |
| 186 | +> |
| 187 | +> Each entry in switcher.json specifies a documentation version and the URL where it can be found. |
| 188 | +> The html_theme_options in conf.py references this file, enabling a dropdown menu to choose the version. |
| 189 | +> The GitHub Actions workflow updates switcher.json upon new releases by prepending the new version into this list. This ensures that the newly released version appears in the version switcher, and users can easily switch to it. |
| 190 | +
|
| 191 | +## 6. Add the Following Files |
| 192 | +
|
| 193 | +- **`doc_requirements.txt`**: Lists the dependencies (Sphinx, sphinx-automodapi, pydata-sphinx-theme, etc.) needed to build documentation. |
| 194 | +
|
| 195 | +- **`docs`** folder: Where documentation content and builds are hosted. This should include **index.html.** (to redirect to the `docs/latest` folder) and **.nojekyll** (to prevent GitHub Pages from ignoring _static and other files that begin with an underscore). |
| 196 | +
|
| 197 | +## 7. Ensure All Special Files Are Set Up in the `docs` Branch |
| 198 | +
|
| 199 | +Ensure that all these files have been correctly prepared: |
| 200 | +- **`docs`** folder: Where documentation content and builds are hosted. This should include **index.html.** (to redirect to the `docs/latest` folder) and **.nojekyll** (to prevent GitHub Pages from ignoring _static and other files that begin with an underscore). |
| 201 | +- **`index.rst`**: Root documentation file linking to `Documentation.rst`. |
| 202 | +- **`conf.py`**: Sphinx configuration file where you set up paths, extensions, and themes. |
| 203 | +- **`generate_doc_markdown.py`**: Automation script that eliminates the need for manual `.rst` creation. |
| 204 | +- **`doc_requirements.txt`**: Lists the dependencies (Sphinx, sphinx-automodapi, pydata-sphinx-theme, etc.) needed to build documentation. |
| 205 | +- **`Makefile`**: Provides convenient commands (`make html`) to build the docs locally. |
| 206 | +- **`_static/switcher.json`**: Used for version switching within the docs (managed by the workflow). |
| 207 | +- **`README.md`**: Copy this readme file as well into the `docs`branch. |
| 208 | +
|
| 209 | +## 8. Add Worflow for Automated Documentation on Release |
| 210 | +
|
| 211 | +Add the `docs.yml` to the `.github/workflows/docs.yml` folder of the branch of your project from which the release will be made. |
| 212 | +
|
| 213 | +In the code below, ensure that PYTHON_PACKAGE, ORGANIZATON and REPO are the correct ones. Other minor changes might be needed (e.g., Python version). |
| 214 | +
|
| 215 | +> This workflow: |
| 216 | +> |
| 217 | +> 1. Checks out the `docs` branch. |
| 218 | +> 2. Sets up Python and installs dependencies from `requirements.txt`. |
| 219 | +> 3. Checks out the release code (tag specified by the release event) into `release-code`. |
| 220 | +> 4. Runs the `generate_doc_markdown.py` script to generate/update `.rst` files. |
| 221 | +> 5. Builds the Sphinx documentation. |
| 222 | +> 6. Copies the built HTML files to `docs/latest` and `docs/{version}` directories. |
| 223 | +> 7. Commits and pushes these changes back to the `docs` branch, updating the live documentation on GitHub Pages. |
| 224 | +> |
| 225 | +> **Example Workflow (in `.github/workflows/docs.yml`):** |
| 226 | +> |
| 227 | +> ```yaml |
| 228 | +> name: Update Documentation |
| 229 | +> |
| 230 | +> on: |
| 231 | +> release: |
| 232 | +> types: |
| 233 | +> - published |
| 234 | +> workflow_dispatch: |
| 235 | +> inputs: |
| 236 | +> test_tag: |
| 237 | +> description: "Release tag to simulate" |
| 238 | +> required: true |
| 239 | +> |
| 240 | +> jobs: |
| 241 | +> update-docs: |
| 242 | +> name: Update Documentation |
| 243 | +> runs-on: ubuntu-latest |
| 244 | +> |
| 245 | +> steps: |
| 246 | +> # Step 1: Check out the docs branch |
| 247 | +> - name: Checkout docs branch |
| 248 | +> uses: actions/checkout@v3 |
| 249 | +> with: |
| 250 | +> ref: docs |
| 251 | +> |
| 252 | +> # Step 2: Set up Python |
| 253 | +> - name: Set up Python |
| 254 | +> uses: actions/setup-python@v4 |
| 255 | +> with: |
| 256 | +> python-version: "3.9" |
| 257 | +> |
| 258 | +> # Step 3: Install dependencies |
| 259 | +> - name: Install dependencies |
| 260 | +> run: | |
| 261 | +> python -m pip install --upgrade pip |
| 262 | +> pip install -r doc_requirements.txt |
| 263 | +> |
| 264 | +> # Step 4: Pull the release code into a separate directory |
| 265 | +> - name: Checkout release code |
| 266 | +> uses: actions/checkout@v3 |
| 267 | +> with: |
| 268 | +> path: release-code |
| 269 | +> # Use the test tag from workflow_dispatch or the actual release tag |
| 270 | +> ref: ${{ github.event.inputs.test_tag || github.event.release.tag_name }} |
| 271 | +> |
| 272 | +> - name: Install the package |
| 273 | +> run: | |
| 274 | +> cd release-code |
| 275 | +> pip install -e . |
| 276 | +> |
| 277 | +> - name: Create the markdown files |
| 278 | +> run: | |
| 279 | +> python generate_doc_markdown.py PYTHON_PACKAGE --exclude=tests,test |
| 280 | +> |
| 281 | +> # Step 5: Set version variable |
| 282 | +> - name: Set version variable |
| 283 | +> run: | |
| 284 | +> VERSION=${{ github.event.inputs.test_tag || github.event.release.tag_name }} |
| 285 | +> echo "VERSION=$VERSION" >> $GITHUB_ENV |
| 286 | +> |
| 287 | +> # Step 6: Update switcher.json |
| 288 | +> - name: Update switcher.json |
| 289 | +> run: | |
| 290 | +> SWITCHER_FILE=_static/switcher.json |
| 291 | +> jq --arg version "$VERSION" \ |
| 292 | +> '. |= [{"name": $version, "version": $version, "url": "https://ORGANIZATION.github.io/REPO/\($version)/"}] + .' \ |
| 293 | +> $SWITCHER_FILE > temp.json && mv temp.json $SWITCHER_FILE |
| 294 | +> |
| 295 | +> # Step 7: Build documentation using Sphinx into html |
| 296 | +> - name: Build documentation |
| 297 | +> env: |
| 298 | +> SPHINX_APIDOC_DIR: release-code |
| 299 | +> run: make html |
| 300 | +> |
| 301 | +> # Step 8: Copy built HTML to `docs/latest` and `docs/{version}` |
| 302 | +> - name: Copy built HTML |
| 303 | +> run: | |
| 304 | +> mkdir -p docs/latest |
| 305 | +> mkdir -p docs/$VERSION |
| 306 | +> cp -r _build/html/* docs/latest/ |
| 307 | +> cp -r _build/html/* docs/$VERSION/ |
| 308 | +> |
| 309 | +> # Step 9: Clean up `release-code` directory |
| 310 | +> - name: Remove release-code directory |
| 311 | +> run: rm -rf release-code |
| 312 | +> |
| 313 | +> # Step 10: Commit and push changes |
| 314 | +> - name: Commit and push changes |
| 315 | +> run: | |
| 316 | +> git config user.name "github-actions[bot]" |
| 317 | +> git config user.email "github-actions[bot]@users.noreply.github.com" |
| 318 | +> git add docs/latest docs/$VERSION _static/switcher.json |
| 319 | +> git commit -m "Update docs for release $VERSION" |
| 320 | +> git push |
| 321 | +> ``` |
| 322 | +
|
| 323 | +## 9. Enable Write Permissions for GitHub Actions |
| 324 | +
|
| 325 | +Ensure that GitHub Actions have write permissions to the repository following these steps: |
| 326 | +- Go to your repository on GitHub. |
| 327 | +- Navigate to Settings > Actions > General. |
| 328 | +- Scroll to the Workflow permissions section. |
| 329 | +- Select Read and write permissions. |
| 330 | +- Click Save. |
0 commit comments