diff --git a/.github/workflows/sphinxbuild.yml b/.github/workflows/sphinxbuild.yml index 65059bec02c..62d1a21946c 100644 --- a/.github/workflows/sphinxbuild.yml +++ b/.github/workflows/sphinxbuild.yml @@ -59,6 +59,50 @@ jobs: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Validate version constants in conf.py + if: github.ref == 'refs/heads/master' || github.base_ref == 'master' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # version_stable: highest stableNN branch whose vN.0.0 release actually exists. + # Mirrors the is_version_released() check in build/build-index.php and stage-and-check. + # A branch that exists but has no release yet (e.g. RC phase) must be skipped. + highest_stable="" + for n in $(git ls-remote --heads origin \ + | sed -n 's?.*refs/heads/stable\([0-9]\{2\}\)$?\1?p' | sort -n -r); do + repo=$( [ "$n" -ge 32 ] && echo "nextcloud-releases/server" || echo "nextcloud/server" ) + status=$(curl -s -o /dev/null -w "%{http_code}" \ + -H "Authorization: token ${GITHUB_TOKEN}" \ + "https://api.github.com/repos/${repo}/releases/tags/v${n}.0.0") + if [ "$status" = "200" ]; then + highest_stable="$n" + break + fi + done + + # version_start: lowest stableNN branch that still exists (branch existence = still documented). + lowest_stable=$(git ls-remote --heads origin \ + | sed -n 's?.*refs/heads/stable\([0-9]\{2\}\)$?\1?p' \ + | sort -n | head -1) + + conf_stable=$(grep -m1 '^\s*version_stable\s*=' conf.py | grep -o '[0-9]\+') + conf_start=$(grep -m1 '^\s*version_start\s*=' conf.py | grep -o '[0-9]\+') + + err=0 + if [ -z "$highest_stable" ]; then + echo "::error::Could not detect any released stable branch." + exit 1 + fi + if [ "$highest_stable" != "$conf_stable" ]; then + echo "::error::version_stable in conf.py ($conf_stable) != highest released stable ($highest_stable). Update conf.py." + err=1 + fi + if [ "$lowest_stable" != "$conf_start" ]; then + echo "::error::version_start in conf.py ($conf_start) != lowest existing stable branch ($lowest_stable). Update conf.py." + err=1 + fi + exit $err + - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: @@ -237,12 +281,17 @@ jobs: - name: Compute PDF release version id: pdf_version + shell: bash run: | - branch="${GITHUB_REF#refs/heads/}" - if [[ "$branch" == stable* ]]; then - echo "release=${branch#stable}" >> $GITHUB_OUTPUT + # For PRs use the target branch; for pushes use the current branch. + # This handles both stable33 direct pushes and backport/*/stable33 PRs. + branch="${GITHUB_BASE_REF:-${GITHUB_REF#refs/heads/}}" + if [[ "$branch" =~ ^stable([0-9]+)$ ]]; then + echo "release=${BASH_REMATCH[1]}" >> $GITHUB_OUTPUT else - echo "release=latest" >> $GITHUB_OUTPUT + # master: derive the dev version from conf.py. + version_stable=$(grep -m1 '^\s*version_stable\s*=' conf.py | grep -o '[0-9]\+') + echo "release=$((version_stable + 1))" >> $GITHUB_OUTPUT fi - name: Build pdf documentation diff --git a/admin_manual/_templates/versions.html b/admin_manual/_templates/versions.html index 70c5e22dee2..b8c514d113e 100644 --- a/admin_manual/_templates/versions.html +++ b/admin_manual/_templates/versions.html @@ -8,14 +8,14 @@
☁️ {{ _('Versions') }}
- {% for slug, url in versions|reverse %} + {% for slug, url, label in versions|reverse %}
- {{ slug }} + {{ label }}
{% endfor %} diff --git a/admin_manual/conf.py b/admin_manual/conf.py index aa7dab6953b..4cbe4abf438 100644 --- a/admin_manual/conf.py +++ b/admin_manual/conf.py @@ -20,7 +20,8 @@ # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information -project = u'Nextcloud %s Administration Manual' % (version) +project = u'Nextcloud %s Administration Manual' % (display_version) +html_title = project # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration diff --git a/conf.py b/conf.py index deec64e0ace..7b5962b2ce3 100644 --- a/conf.py +++ b/conf.py @@ -56,35 +56,61 @@ # disable including the reST sources in HTML builds (in _sources/) (default is True) html_copy_source = False +# building the versions list +# Update version_start when the lowest stableNN branch is deleted (version goes EoL). +# Update version_stable when a new NC release ships (highest stableNN branch added). +version_start = 32 # oldest documented version + + # latest released stable — CHANGING IT MUST RESULT IN A CHANGE OF THE SYMLINK ON THE LIVE SERVER +version_stable = 34 # mapped to https://docs.nextcloud.com/server/stable/ + +import re as _re +# Detect stable branch version for display purposes. +# For PRs: GITHUB_BASE_REF is the target branch (e.g. 'stable33'). +# For direct pushes: GITHUB_REF is 'refs/heads/stable33'. +_base = os.environ.get('GITHUB_BASE_REF', '') +_ref = os.environ.get('GITHUB_REF', '') +_stable_ver = ( + _re.match(r'^stable(\d+)$', _base) + or _re.match(r'^refs/heads/stable(\d+)$', _ref) +) +display_version = ( + release if release != 'latest' # PDF/ePub builds (DOCS_RELEASE set) + else _stable_ver.group(1) if _stable_ver # stableNN branches and PRs targeting them + else str(version_stable + 1) # master +) + +# Also search for "TODO ON RELEASE" in the rst files + # substitutions go here rst_epilog = """ .. |version| replace:: %s -""" % (release) +""" % (display_version) # Replace hardcoded /latest/ URLs in all .rst source files with the actual release def replace_latest(app, docname, source): if release != 'latest': source[0] = source[0].replace('/server/latest/', '/server/%s/' % release) - + def setup(app): app.connect('source-read', replace_latest) - -# building the versions list -version_start = 31 # THIS IS THE OLDEST SUPPORTED VERSION NUMBER - - # THIS IS THE VERSION THAT IS MAPPED TO https://docs.nextcloud.com/server/stable/ -version_stable = 32 # CHANGING IT MUST RESULT IN A CHANGE OF THE SYMLINK ON THE LIVE SERVER - -# Also search for "TODO ON RELEASE" in the rst files - def generateVersionsDocs(current_docs): versions_doc = [] - for v in range(version_start, version_stable + 1): + + # If viewing an unsupported (older than version_start) branch, prepend it so it + # appears last after the template's |reverse — e.g. "26 (unsupported)" at the bottom. + if _stable_ver: + branch_ver = int(_stable_ver.group(1)) + if branch_ver < version_start: + url = 'https://docs.nextcloud.com/server/%s/%s' % (str(branch_ver), current_docs) + versions_doc.append((branch_ver, url, '%s (unsupported)' % branch_ver)) + + for v in range(version_start, version_stable): url = 'https://docs.nextcloud.com/server/%s/%s' % (str(v), current_docs) - versions_doc.append(tuple((v, url))) - versions_doc.append(tuple(('stable', 'https://docs.nextcloud.com/server/%s/%s' % ('stable', current_docs)))) - versions_doc.append(tuple(('latest', 'https://docs.nextcloud.com/server/%s/%s' % ('latest', current_docs)))) + versions_doc.append((v, url, str(v))) + versions_doc.append(('stable', 'https://docs.nextcloud.com/server/stable/%s' % current_docs, '%s (stable)' % version_stable)) + versions_doc.append(('latest', 'https://docs.nextcloud.com/server/latest/%s' % current_docs, '%s (latest)' % str(version_stable + 1))) return versions_doc if version.isdigit(): @@ -93,7 +119,8 @@ def generateVersionsDocs(current_docs): github_branch = 'master' html_context = { - 'current_version': version, + 'current_version': int(_stable_ver.group(1)) if _stable_ver else version, + 'display_version': display_version, 'READTHEDOCS': True, # force github plugin diff --git a/developer_manual/_templates/versions.html b/developer_manual/_templates/versions.html index 70c5e22dee2..b8c514d113e 100644 --- a/developer_manual/_templates/versions.html +++ b/developer_manual/_templates/versions.html @@ -8,14 +8,14 @@
☁️ {{ _('Versions') }}
- {% for slug, url in versions|reverse %} + {% for slug, url, label in versions|reverse %}
- {{ slug }} + {{ label }}
{% endfor %} diff --git a/developer_manual/conf.py b/developer_manual/conf.py index 61d72760015..25071039ccf 100644 --- a/developer_manual/conf.py +++ b/developer_manual/conf.py @@ -22,7 +22,8 @@ # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information -project = "Nextcloud %s Developer Manual" % (version) +project = "Nextcloud %s Developer Manual" % (display_version) +html_title = project # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration diff --git a/user_manual/_templates/versions.html b/user_manual/_templates/versions.html index 78076e86268..39b7dda82db 100644 --- a/user_manual/_templates/versions.html +++ b/user_manual/_templates/versions.html @@ -83,14 +83,14 @@
☁️ {{ _('Versions') }}
- {% for slug, url in versions|reverse %} + {% for slug, url, label in versions|reverse %}
- {{ slug }} + {{ label }}
{% endfor %} diff --git a/user_manual/conf.py b/user_manual/conf.py index d0b1e0f99b6..9a6382e5d82 100644 --- a/user_manual/conf.py +++ b/user_manual/conf.py @@ -20,7 +20,8 @@ # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information -project = u'Nextcloud %s User Manual' % (version) +project = u'Nextcloud %s User Manual' % (display_version) +html_title = project # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration @@ -55,7 +56,7 @@ ## Markup # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-markup # a substitution that will be included in every source file -rst_epilog = '.. |version| replace:: %s' % version +rst_epilog = '.. |version| replace:: %s' % display_version # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output