Skip to content

Commit a816525

Browse files
committed
store multiversion
1 parent 8d1ca56 commit a816525

File tree

5 files changed

+231
-100
lines changed

5 files changed

+231
-100
lines changed

.github/workflows/CI.yml

Lines changed: 84 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,20 @@ name: CI
22

33
on:
44
push:
5+
tags:
6+
- 'v[0-9]*.[0-9]*.[0-9]*'
7+
- 'v[0-9]*.[0-9]*.[0-9]*a[0-9]*'
8+
- 'v[0-9]*.[0-9]*.[0-9]*b[0-9]*'
9+
- 'v[0-9]*.[0-9]*.[0-9]*rc[0-9]*'
510
branches:
611
- master
12+
- next2026
13+
- verdocs
714
pull_request:
815
branches:
916
- master
17+
- next2026
18+
- verdocs
1019
schedule:
1120
- cron: "9 16 * * 1"
1221

@@ -19,108 +28,13 @@ jobs:
1928
fail-fast: false
2029
matrix:
2130
cfg:
22-
- conda-env: psi
23-
python-version: 3.7
24-
label: Psi4-1.5
25-
runs-on: ubuntu-latest
26-
pytest: ""
27-
28-
- conda-env: psi-nightly
29-
python-version: "3.10"
30-
label: Psi4-1.6
31-
runs-on: ubuntu-latest
32-
pytest: ""
33-
34-
- conda-env: psi-cf
35-
python-version: "3.12"
36-
label: Psi4-1.8
37-
runs-on: windows-latest
38-
pytest: "-k 'not (hes2 or qchem or test_config)'"
39-
40-
- conda-env: torchani
41-
python-version: 3.8
42-
label: ANI
43-
runs-on: ubuntu-latest
44-
pytest: ""
45-
46-
- conda-env: openmm
47-
python-version: 3.8
48-
label: OpenMM
49-
runs-on: ubuntu-latest
50-
pytest: ""
51-
52-
- conda-env: openmm
53-
python-version: 3.12
54-
label: OpenMM
55-
runs-on: ubuntu-latest
56-
pytest: ""
5731

5832
- conda-env: xtb
5933
python-version: "3.10"
6034
label: xTB
6135
runs-on: ubuntu-latest
6236
pytest: ""
6337

64-
- conda-env: qcore
65-
python-version: 3.7
66-
label: QCore
67-
runs-on: ubuntu-latest
68-
pytest: ""
69-
70-
- conda-env: nwchem
71-
python-version: 3.8
72-
label: NWChem70
73-
runs-on: ubuntu-latest
74-
pytest: ""
75-
# formerly NWChem v6.6 with python-version: 3.6 & runs-on: ubuntu-16.04 but ubuntu env retired by GH Sep 2021
76-
77-
- conda-env: nwchem-cf
78-
python-version: 3.12
79-
label: NWChem
80-
runs-on: ubuntu-latest
81-
pytest: ""
82-
83-
- conda-env: mrchem
84-
python-version: 3.8
85-
label: MRChem
86-
runs-on: ubuntu-latest
87-
pytest: ""
88-
89-
- conda-env: adcc
90-
python-version: 3.8
91-
label: ADCC
92-
runs-on: ubuntu-latest
93-
pytest: ""
94-
95-
- conda-env: opt-disp
96-
python-version: 3.13
97-
label: optimization-dispersion
98-
runs-on: ubuntu-latest
99-
pytest: ""
100-
101-
- conda-env: opt-disp-cf
102-
python-version: 3.11
103-
label: optimization-dispersion
104-
runs-on: windows-latest
105-
pytest: "-k 'not (hes2 or qchem or test_config)'"
106-
107-
- conda-env: mace
108-
python-version: "3.10"
109-
label: MACE
110-
runs-on: ubuntu-latest
111-
pytest: ""
112-
113-
- conda-env: aimnet2
114-
python-version: 3.11
115-
label: AIMNET2
116-
runs-on: ubuntu-latest
117-
pytest: ""
118-
119-
- conda-env: opt-disp-cf
120-
python-version: 3.12
121-
label: QCSk-next
122-
runs-on: ubuntu-latest
123-
pytest: ""
12438

12539
name: "🐍 ${{ matrix.cfg.python-version }} • ${{ matrix.cfg.label }} • ${{ matrix.cfg.runs-on }}"
12640
runs-on: ${{ matrix.cfg.runs-on }}
@@ -280,9 +194,82 @@ jobs:
280194
cd docs
281195
make html
282196
197+
- name: Compute docs deploy target
198+
id: target
199+
shell: bash
200+
run: |
201+
set -euo pipefail
202+
203+
echo GITHUB_REF_TYPE ${GITHUB_REF_TYPE}
204+
echo GITHUB_REF_NAME ${GITHUB_REF_NAME}
205+
echo GITHUB_REF ${GITHUB_REF}
206+
if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
207+
echo "target_folder=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
208+
echo "version_label=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
209+
210+
elif [[ "${GITHUB_REF_NAME}" == "master" || "${GITHUB_REF_NAME}" == "verdocs" ]]; then
211+
echo "target_folder=dev" >> "$GITHUB_OUTPUT"
212+
echo "version_label=dev" >> "$GITHUB_OUTPUT"
213+
214+
elif [[ "${GITHUB_REF_NAME}" == "next2026" ]]; then
215+
echo "target_folder=next" >> "$GITHUB_OUTPUT"
216+
echo "version_label=next" >> "$GITHUB_OUTPUT"
217+
218+
else
219+
echo "target_folder=" >> "$GITHUB_OUTPUT"
220+
echo "version_label=" >> "$GITHUB_OUTPUT"
221+
fi
222+
echo GITHUB_OUTPUT ${GITHUB_OUTPUT}
223+
283224
- name: GitHub Pages Deploy
284-
uses: JamesIves/github-pages-deploy-action@4.1.1
285-
if: github.event_name == 'push' && github.repository == 'MolSSI/QCEngine' && ( startsWith( github.ref, 'refs/tags/' ) || github.ref == 'refs/heads/master' )
225+
uses: JamesIves/github-pages-deploy-action@v4
226+
if: github.event_name == 'push' && github.repository == 'MolSSI/QCEngine' && ( startsWith( github.ref, 'refs/tags/' ) || github.ref == 'refs/heads/master' || github.ref == 'refs/heads/verdocs' )
286227
with:
287228
branch: gh-pages
288229
folder: docs/build/html
230+
target-folder: ${{ steps.target.outputs.target_folder }}
231+
clean: false
232+
233+
- name: Update versions.json
234+
if: github.event_name == 'push' && github.repository == 'MolSSI/QCEngine' && ( startsWith( github.ref, 'refs/tags/' ) || github.ref == 'refs/heads/master' || github.ref == 'refs/heads/verdocs' )
235+
shell: bash
236+
env:
237+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
238+
VERSION_LABEL: ${{ steps.target.outputs.version_label }}
239+
TARGET_FOLDER: ${{ steps.target.outputs.target_folder }}
240+
run: |
241+
set -euo pipefail
242+
rm -rf _ghp
243+
git clone --depth 1 --branch gh-pages "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" _ghp
244+
cd _ghp
245+
# Create versions.json if it doesn't exist yet
246+
if [[ ! -f versions.json ]]; then
247+
cat > versions.json <<'JSON'
248+
[
249+
{"label": "dev", "path": "dev/"},
250+
{"label": "next", "path": "next/"}
251+
]
252+
JSON
253+
fi
254+
python - <<'PY'
255+
import json, pathlib, os
256+
p = pathlib.Path("versions.json")
257+
data = json.loads(p.read_text())
258+
pinned = [
259+
{"label": "dev", "path": "dev/"},
260+
{"label": "next", "path": "next/"},
261+
]
262+
lbl = os.environ.get("VERSION_LABEL", "")
263+
tgt = os.environ.get("TARGET_FOLDER", "")
264+
if lbl and lbl not in ("dev", "next") and tgt:
265+
pinned.insert(1, {"label": lbl, "path": tgt.rstrip("/") + "/"})
266+
p.write_text(json.dumps(pinned, indent=2) + "\n")
267+
PY
268+
if ! git diff --quiet; then
269+
git config user.name "github-actions[bot]"
270+
git config user.email "github-actions[bot]@users.noreply.github.com"
271+
git add versions.json
272+
git commit -m "docs: update versions.json (${GITHUB_REF})"
273+
git push origin gh-pages
274+
fi
275+

docs/source/_extra/versions.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[
2+
{"label":"dev","path":"dev/"},
3+
{"label":"next","path":"next/"}
4+
]
5+
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
(async function () {
2+
function sleep(ms) {
3+
return new Promise((r) => setTimeout(r, ms));
4+
}
5+
6+
// Wait for RTD sidebar to exist (theme may build it after our script loads)
7+
async function waitForSidebar(maxTries = 50, delayMs = 100) {
8+
for (let i = 0; i < maxTries; i++) {
9+
const search = document.querySelector(".wy-side-nav-search");
10+
const scroll = document.querySelector(".wy-side-scroll");
11+
if (search || scroll) return { search, scroll };
12+
await sleep(delayMs);
13+
}
14+
return { search: null, scroll: null };
15+
}
16+
17+
const { search, scroll } = await waitForSidebar();
18+
if (!search && !scroll) {
19+
console.warn("[version-switcher] RTD sidebar not found.");
20+
return;
21+
}
22+
23+
// Avoid duplicates (important if theme re-renders and script runs again)
24+
if (document.querySelector(".version-switcher")) return;
25+
26+
// --- UI ---
27+
const container = document.createElement("div");
28+
container.className = "version-switcher";
29+
container.style.padding = "0.5rem 1rem";
30+
container.style.display = "flex";
31+
container.style.gap = "0.5rem";
32+
container.style.alignItems = "center";
33+
container.style.justifyContent = "center";
34+
35+
const label = document.createElement("span");
36+
label.textContent = "Version:";
37+
label.style.fontSize = "0.9em";
38+
label.className = "vs-label";
39+
40+
const select = document.createElement("select");
41+
select.setAttribute("aria-label", "Select documentation version");
42+
select.style.width = "50%";
43+
select.style.borderRadius = "20px";
44+
45+
container.appendChild(label);
46+
container.appendChild(select);
47+
48+
// Insert under the search box if possible
49+
if (search && search.parentNode) {
50+
search.insertAdjacentElement("afterend", container);
51+
} else {
52+
scroll.prepend(container);
53+
}
54+
55+
// --- Load versions.json ---
56+
select.disabled = true;
57+
select.innerHTML = `<option>Loading…</option>`;
58+
59+
async function fetchJson(url) {
60+
const resp = await fetch(url, { cache: "no-store" });
61+
if (!resp.ok) throw new Error(`${resp.status} ${resp.statusText}`);
62+
return resp.json();
63+
}
64+
65+
// For local http://localhost:8000/ this will work.
66+
// For GitHub project pages it will also work if versions.json is at the root of the published site.
67+
let versions;
68+
try {
69+
versions = await fetchJson(`${window.location.origin}/versions.json`);
70+
} catch (e) {
71+
// GitHub project page fallback: /QCElemental/versions.json
72+
const parts = window.location.pathname.split("/").filter(Boolean);
73+
if (parts.length) {
74+
try {
75+
versions = await fetchJson(`${window.location.origin}/${parts[0]}/versions.json`);
76+
} catch (e2) {
77+
console.warn("[version-switcher] Could not load versions.json", e, e2);
78+
select.innerHTML = `<option>No versions.json</option>`;
79+
return;
80+
}
81+
} else {
82+
console.warn("[version-switcher] Could not load versions.json", e);
83+
select.innerHTML = `<option>No versions.json</option>`;
84+
return;
85+
}
86+
}
87+
88+
// Populate
89+
select.innerHTML = "";
90+
for (const v of versions) {
91+
const opt = document.createElement("option");
92+
opt.value = v.path; // "dev/" etc.
93+
opt.textContent = v.label; // "dev"
94+
select.appendChild(opt);
95+
}
96+
97+
// Determine current "version folder" (dev / v0.30.1 / etc.)
98+
// If you're at /dev/index.html => currentVersion = "dev"
99+
// If you're at /index.html => currentVersion = ""
100+
const pathParts = window.location.pathname.split("/").filter(Boolean);
101+
const currentVersion = pathParts.length >= 2 ? pathParts[1] : pathParts[0] || "";
102+
103+
for (const opt of select.options) {
104+
if (opt.value.replace(/\/+$/, "") === currentVersion) {
105+
opt.selected = true;
106+
break;
107+
}
108+
}
109+
110+
select.disabled = false;
111+
112+
// Navigate on change; for local dev it’s simplest to go to version root
113+
select.addEventListener("change", () => {
114+
const target = select.value; // e.g. "dev/"
115+
// If on GitHub project site, keep the project prefix automatically.
116+
const parts = window.location.pathname.split("/").filter(Boolean);
117+
const prefix = parts.length ? `/${parts[0]}/` : "/";
118+
window.location.href = `${window.location.origin}${prefix}${target}`;
119+
});
120+
})();
121+

docs/source/changelog.rst

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,16 @@ Changelog
2828
.. - UNSOLVED (:issue:`397`) extras failed
2929
3030
31-
v0.35.0 / 2026-MM-DD (Unreleased)
32-
--------------------
31+
.. _`sec:cl0341`:
32+
33+
0.34.1 / 2026-02-14
34+
-------------------
35+
36+
`Docs <https://MolSSI.github.io/QCEngine/v0.34.1/>`_
37+
38+
Enhancements
39+
++++++++++++
40+
- (:pr:`???`) Set up documentation build to store versions of documentation for each tag plus dev.
3341

3442
Misc.
3543
+++++

docs/source/conf.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,22 @@
112112
# further. For a list of options available for each theme, see the
113113
# documentation.
114114
#
115-
# html_theme_options = {}
115+
html_theme_options = {
116+
# keep these off unless you're actually on RTD
117+
"version_selector": False,
118+
"language_selector": False,
119+
}
116120

117121
# Add any paths that contain custom static files (such as style sheets) here,
118122
# relative to this directory. They are copied after the builtin static files,
119123
# so a file named "default.css" will overwrite the builtin "default.css".
120124
html_static_path = ['_static']
125+
html_extra_path = ["_extra"]
126+
127+
128+
def setup(app):
129+
app.add_js_file("version-switcher.js")
130+
121131

122132
# Custom sidebar templates, must be a dictionary that maps document names
123133
# to template names.

0 commit comments

Comments
 (0)