Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 81 additions & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@ name: CI

on:
push:
tags:
- 'v[0-9]*.[0-9]*.[0-9]*'
- 'v[0-9]*.[0-9]*.[0-9]*a[0-9]*'
- 'v[0-9]*.[0-9]*.[0-9]*b[0-9]*'
- 'v[0-9]*.[0-9]*.[0-9]*rc[0-9]*'
branches:
- master
- next2026
pull_request:
branches:
- master
- next2026
schedule:
- cron: "9 16 * * 1"

Expand Down Expand Up @@ -280,9 +287,82 @@ jobs:
cd docs
make html

- name: Compute docs deploy target
id: target
shell: bash
run: |
set -euo pipefail

echo GITHUB_REF_TYPE ${GITHUB_REF_TYPE}
echo GITHUB_REF_NAME ${GITHUB_REF_NAME}
echo GITHUB_REF ${GITHUB_REF}
if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
echo "target_folder=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
echo "version_label=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"

elif [[ "${GITHUB_REF_NAME}" == "master" ]]; then
echo "target_folder=dev" >> "$GITHUB_OUTPUT"
echo "version_label=dev" >> "$GITHUB_OUTPUT"

elif [[ "${GITHUB_REF_NAME}" == "next2026" ]]; then
echo "target_folder=next" >> "$GITHUB_OUTPUT"
echo "version_label=next" >> "$GITHUB_OUTPUT"

else
echo "target_folder=" >> "$GITHUB_OUTPUT"
echo "version_label=" >> "$GITHUB_OUTPUT"
fi
echo GITHUB_OUTPUT ${GITHUB_OUTPUT}

- name: GitHub Pages Deploy
uses: JamesIves/github-pages-deploy-action@4.1.1
uses: JamesIves/github-pages-deploy-action@v4
if: github.event_name == 'push' && github.repository == 'MolSSI/QCEngine' && ( startsWith( github.ref, 'refs/tags/' ) || github.ref == 'refs/heads/master' )
with:
branch: gh-pages
folder: docs/build/html
target-folder: ${{ steps.target.outputs.target_folder }}
clean: false

- name: Update versions.json
if: github.event_name == 'push' && github.repository == 'MolSSI/QCEngine' && ( startsWith( github.ref, 'refs/tags/' ) || github.ref == 'refs/heads/master' )
shell: bash
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION_LABEL: ${{ steps.target.outputs.version_label }}
TARGET_FOLDER: ${{ steps.target.outputs.target_folder }}
run: |
set -euo pipefail
rm -rf _ghp
git clone --depth 1 --branch gh-pages "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" _ghp
cd _ghp
# Create versions.json if it doesn't exist yet
if [[ ! -f versions.json ]]; then
cat > versions.json <<'JSON'
[
{"label": "dev", "path": "dev/"},
{"label": "next", "path": "next/"}
]
JSON
fi
python - <<'PY'
import json, pathlib, os
p = pathlib.Path("versions.json")
data = json.loads(p.read_text())
pinned = [
{"label": "dev", "path": "dev/"},
{"label": "next", "path": "next/"},
]
lbl = os.environ.get("VERSION_LABEL", "")
tgt = os.environ.get("TARGET_FOLDER", "")
if lbl and lbl not in ("dev", "next") and tgt:
pinned.insert(1, {"label": lbl, "path": tgt.rstrip("/") + "/"})
p.write_text(json.dumps(pinned, indent=2) + "\n")
PY
if ! git diff --quiet; then
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add versions.json
git commit -m "docs: update versions.json (${GITHUB_REF})"
git push origin gh-pages
fi

5 changes: 5 additions & 0 deletions docs/source/_extra/versions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[
{"label":"dev","path":"dev/"},
{"label":"next","path":"next/"}
]

121 changes: 121 additions & 0 deletions docs/source/_static/version-switcher.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
(async function () {
function sleep(ms) {
return new Promise((r) => setTimeout(r, ms));
}

// Wait for RTD sidebar to exist (theme may build it after our script loads)
async function waitForSidebar(maxTries = 50, delayMs = 100) {
for (let i = 0; i < maxTries; i++) {
const search = document.querySelector(".wy-side-nav-search");
const scroll = document.querySelector(".wy-side-scroll");
if (search || scroll) return { search, scroll };
await sleep(delayMs);
}
return { search: null, scroll: null };
}

const { search, scroll } = await waitForSidebar();
if (!search && !scroll) {
console.warn("[version-switcher] RTD sidebar not found.");
return;
}

// Avoid duplicates (important if theme re-renders and script runs again)
if (document.querySelector(".version-switcher")) return;

// --- UI ---
const container = document.createElement("div");
container.className = "version-switcher";
container.style.padding = "0.5rem 1rem";
container.style.display = "flex";
container.style.gap = "0.5rem";
container.style.alignItems = "center";
container.style.justifyContent = "center";

const label = document.createElement("span");
label.textContent = "Version:";
label.style.fontSize = "0.9em";
label.className = "vs-label";

const select = document.createElement("select");
select.setAttribute("aria-label", "Select documentation version");
select.style.width = "50%";
select.style.borderRadius = "20px";

container.appendChild(label);
container.appendChild(select);

// Insert under the search box if possible
if (search && search.parentNode) {
search.insertAdjacentElement("afterend", container);
} else {
scroll.prepend(container);
}

// --- Load versions.json ---
select.disabled = true;
select.innerHTML = `<option>Loading…</option>`;

async function fetchJson(url) {
const resp = await fetch(url, { cache: "no-store" });
if (!resp.ok) throw new Error(`${resp.status} ${resp.statusText}`);
return resp.json();
}

// For local http://localhost:8000/ this will work.
// For GitHub project pages it will also work if versions.json is at the root of the published site.
let versions;
try {
versions = await fetchJson(`${window.location.origin}/versions.json`);
} catch (e) {
// GitHub project page fallback: /QCElemental/versions.json
const parts = window.location.pathname.split("/").filter(Boolean);
if (parts.length) {
try {
versions = await fetchJson(`${window.location.origin}/${parts[0]}/versions.json`);
} catch (e2) {
console.warn("[version-switcher] Could not load versions.json", e, e2);
select.innerHTML = `<option>No versions.json</option>`;
return;
}
} else {
console.warn("[version-switcher] Could not load versions.json", e);
select.innerHTML = `<option>No versions.json</option>`;
return;
}
}

// Populate
select.innerHTML = "";
for (const v of versions) {
const opt = document.createElement("option");
opt.value = v.path; // "dev/" etc.
opt.textContent = v.label; // "dev"
select.appendChild(opt);
}

// Determine current "version folder" (dev / v0.30.1 / etc.)
// If you're at /dev/index.html => currentVersion = "dev"
// If you're at /index.html => currentVersion = ""
const pathParts = window.location.pathname.split("/").filter(Boolean);
const currentVersion = pathParts.length >= 2 ? pathParts[1] : pathParts[0] || "";

for (const opt of select.options) {
if (opt.value.replace(/\/+$/, "") === currentVersion) {
opt.selected = true;
break;
}
}

select.disabled = false;

// Navigate on change; for local dev it’s simplest to go to version root
select.addEventListener("change", () => {
const target = select.value; // e.g. "dev/"
// If on GitHub project site, keep the project prefix automatically.
const parts = window.location.pathname.split("/").filter(Boolean);
const prefix = parts.length ? `/${parts[0]}/` : "/";
window.location.href = `${window.location.origin}${prefix}${target}`;
});
})();

19 changes: 17 additions & 2 deletions docs/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,23 @@ Changelog
.. - UNSOLVED (:issue:`397`) extras failed
v0.35.0 / 2026-MM-DD (Unreleased)
--------------------
.. _`sec:cl0341`:

0.34.1 / 2026-02-15
-------------------

`Docs <https://MolSSI.github.io/QCEngine/v0.34.1/>`_

Enhancements
++++++++++++
- (:pr:`492`) Docs - Set up documentation build to store versions of documentation for each tag plus dev.
Note that this changes intersphinx links. Below is current.

.. code:: python
"qcelemental": ("https://molssi.github.io/QCElemental/dev/", None),
"qcengine": ("https://molssi.github.io/QCEngine/dev/", None),
"qcfractal": ("https://molssi.github.io/QCFractal/", None),
Misc.
+++++
Expand Down
17 changes: 13 additions & 4 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,22 @@
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
html_theme_options = {
# keep these off unless you're actually on RTD
"version_selector": False,
"language_selector": False,
}

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_extra_path = ["_extra"]


def setup(app):
app.add_js_file("version-switcher.js")


# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
Expand Down Expand Up @@ -201,9 +211,8 @@
"numpy": ("https://numpy.org/doc/stable/", None),
'scipy': ('https://docs.scipy.org/doc/scipy/', None),
'matplotlib': ('https://matplotlib.org/stable/', None),
"qcelemental": ("http://docs.qcarchive.molssi.org/projects/QCElemental/en/latest/", None),
"qcportal": ("http://docs.qcarchive.molssi.org/projects/QCPortal/en/latest/", None),
"qcfractal": ("http://docs.qcarchive.molssi.org/projects/QCFractal/en/latest/", None),
"qcelemental": ("https://molssi.github.io/QCElemental/dev/", None),
"qcfractal": ("https://molssi.github.io/QCFractal/", None),
}

# -- Options for todo extension ----------------------------------------------
Expand Down
Loading