diff options
| author | Yuriy Andamasov <yuriy@vyos.io> | 2026-05-10 15:51:43 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-05-10 15:51:43 +0300 |
| commit | 739043c260645d507adb756ca27e952bdfbad858 (patch) | |
| tree | 1d805b4c748e12c084e63356015016ee513008f8 | |
| parent | 1a1aacfe67699ab468fbb5672d9610af80dabc76 (diff) | |
| parent | 1bf386e2ce6e4039f9adbef868ef54758e4204c0 (diff) | |
| download | vyos-documentation-739043c260645d507adb756ca27e952bdfbad858.tar.gz vyos-documentation-739043c260645d507adb756ca27e952bdfbad858.zip | |
Merge pull request #1938 from vyos/yuriy/cleanup-rolling-references
ci/docs: drop current branch references after rename to rolling
| -rw-r--r-- | .github/PULL_REQUEST_TEMPLATE.md | 2 | ||||
| -rw-r--r-- | .github/workflows/ai-validation.yml | 2 | ||||
| -rw-r--r-- | .github/workflows/check-open-prs-conflict.yml | 1 | ||||
| -rw-r--r-- | .github/workflows/lint-doc.yml | 34 | ||||
| -rw-r--r-- | .github/workflows/pr-mirror-repo-sync.yml | 11 | ||||
| -rw-r--r-- | .github/workflows/submodules.yml | 8 | ||||
| -rw-r--r-- | .github/workflows/update-translations.yml | 2 | ||||
| -rw-r--r-- | .github/workflows/update-version-tags.yml | 3 | ||||
| -rw-r--r-- | README.md | 6 | ||||
| -rw-r--r-- | docs/conf.py | 10 | ||||
| -rw-r--r-- | scripts/doc-linter.py | 177 |
11 files changed, 228 insertions, 28 deletions
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 2c1dd640..61a1c45e 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -19,4 +19,4 @@ ## Checklist: <!--- Go over all the following points, and put an `x` in all the boxes that apply. --> <!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! --> -- [ ] I have read the [**CONTRIBUTING**](https://github.com/vyos/vyos-documentation/blob/current/CONTRIBUTING.md) document
\ No newline at end of file +- [ ] I have read the [**CONTRIBUTING**](https://github.com/vyos/vyos-documentation/blob/rolling/CONTRIBUTING.md) document
\ No newline at end of file diff --git a/.github/workflows/ai-validation.yml b/.github/workflows/ai-validation.yml index a5d120e7..60964396 100644 --- a/.github/workflows/ai-validation.yml +++ b/.github/workflows/ai-validation.yml @@ -150,7 +150,7 @@ jobs: IMPORTANT: This PR targets the **${{ steps.branch.outputs.target }}** branch. The vyos-1x checkout matches this branch. Features may differ between branches - (e.g., a command exists in `current` but not in `sagitta`). Only flag issues + (e.g., a command exists in `rolling` but not in `sagitta`). Only flag issues relevant to this specific branch. ## Pass 1 Findings diff --git a/.github/workflows/check-open-prs-conflict.yml b/.github/workflows/check-open-prs-conflict.yml index 3aa9ce05..9667fd62 100644 --- a/.github/workflows/check-open-prs-conflict.yml +++ b/.github/workflows/check-open-prs-conflict.yml @@ -2,7 +2,6 @@ name: "Open PRs Conflicts checker" on: push: branches: - - current - rolling - sagitta - circinus diff --git a/.github/workflows/lint-doc.yml b/.github/workflows/lint-doc.yml index b5ba4688..d7f25f38 100644 --- a/.github/workflows/lint-doc.yml +++ b/.github/workflows/lint-doc.yml @@ -2,9 +2,33 @@ name: Lint Doc on: pull_request: +permissions: + contents: read + pull-requests: read + jobs: - lint-doc: - uses: vyos/.github/.github/workflows/lint-doc.yml@current - secrets: inherit - - + doc-lint: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Bullfrog Secure Runner + continue-on-error: true + uses: bullfrogsec/bullfrog@v0.8.4 + with: + egress-policy: audit + + - name: Get File Changes + id: file_changes + uses: trilom/file-changes-action@v1.2.4 + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: '3.x' + + - name: run doc linter + env: + FILES_MODIFIED: ${{ steps.file_changes.outputs.files_modified }} + run: python scripts/doc-linter.py "$FILES_MODIFIED" diff --git a/.github/workflows/pr-mirror-repo-sync.yml b/.github/workflows/pr-mirror-repo-sync.yml index 1f01bd02..5c944971 100644 --- a/.github/workflows/pr-mirror-repo-sync.yml +++ b/.github/workflows/pr-mirror-repo-sync.yml @@ -3,16 +3,15 @@ name: PR Mirror and Repo Sync on:
pull_request_target:
types: [closed]
- branches: [current, rolling]
+ branches: [rolling]
workflow_dispatch:
inputs:
sync_branch:
description: 'Branch to mirror'
required: true
- default: 'current'
+ default: 'rolling'
type: choice
options:
- - current
- rolling
permissions:
@@ -28,9 +27,9 @@ jobs: github.event_name == 'workflow_dispatch' ||
(github.event_name == 'pull_request_target' && github.event.pull_request.merged == true)
)
- uses: vyos/.github/.github/workflows/pr-mirror-repo-sync.yml@current - with: - sync_branch: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.base.ref || github.event.inputs.sync_branch || 'current' }} + uses: vyos/.github/.github/workflows/pr-mirror-repo-sync.yml@current
+ with:
+ sync_branch: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.base.ref || github.event.inputs.sync_branch || 'rolling' }}
secrets:
PAT: ${{ secrets.PAT }}
REMOTE_OWNER: ${{ secrets.REMOTE_OWNER }}
diff --git a/.github/workflows/submodules.yml b/.github/workflows/submodules.yml index 24b6c505..16ba1ab5 100644 --- a/.github/workflows/submodules.yml +++ b/.github/workflows/submodules.yml @@ -5,7 +5,7 @@ on: # 06:00 UTC on Monday - cron: '0 6 * * 1' jobs: - update_current: + update_rolling: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -31,12 +31,12 @@ jobs: uses: peter-evans/create-pull-request@v6 with: token: ${{ secrets.PR_ACTION_DOCUMENTATION_SUBMODULE_UPDATE }} - commit-message: "Github: update current branch" - title: "Github: update current branch" + commit-message: "GitHub: update rolling branch" + title: "GitHub: update rolling branch" body: | Autoupdate vyos-1x submodule update releasenotes - branch: update-dependencies-current + branch: update-dependencies-rolling delete-branch: true update_sagitta: diff --git a/.github/workflows/update-translations.yml b/.github/workflows/update-translations.yml index 6ab53323..4b0bafa1 100644 --- a/.github/workflows/update-translations.yml +++ b/.github/workflows/update-translations.yml @@ -57,6 +57,6 @@ jobs: title: "Github: update translations" body: | Generate, upload new and download translation files - branch: update-translations-current + branch: update-translations-rolling delete-branch: true diff --git a/.github/workflows/update-version-tags.yml b/.github/workflows/update-version-tags.yml index c7c372a3..7f179586 100644 --- a/.github/workflows/update-version-tags.yml +++ b/.github/workflows/update-version-tags.yml @@ -3,7 +3,6 @@ name: Update version tags on: push: branches: - - current - rolling - circinus - sagitta @@ -28,7 +27,7 @@ jobs: run: | set -euo pipefail case "$BRANCH" in - current|rolling) TAG=rolling ;; + rolling) TAG=rolling ;; circinus) TAG=1.5 ;; sagitta) TAG=1.4 ;; *) echo "Unexpected branch: $BRANCH" >&2; exit 1 ;; @@ -11,10 +11,10 @@ Our old wiki with documentation from the VyOS 1.1.x and early 1.2.0 era can stil Our documentation repository follows the same branching scheme as the VyOS source itself. We maintain one documentation branch per VyOS release. -The default branch that contains the most recent VyOS documentation is called `current` -and matches the latest VyOS rolling release. +The default branch that contains the most recent VyOS documentation is called `rolling` +(renamed from `current` on 2026-05-10) and matches the latest VyOS rolling release. -All new documentation enhancements go to the `current` branch. If those changes +All new documentation enhancements go to the `rolling` branch. If those changes are beneficial for previous VyOS documentation versions they will be cherry-picked to the appropriate branch(es). diff --git a/docs/conf.py b/docs/conf.py index 083482a8..ea3c5b61 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -29,12 +29,14 @@ author = u'VyOS maintainers and contributors' # The short X.Y version (rolling — next major; bumped at branch cut) version = u'rolling' -# The full version, including alpha/beta/rc tags. The `current` branch +# The full version, including alpha/beta/rc tags. The `rolling` branch # is the rolling tip and serves at /en/rolling/ on RTD; the literal # below is exposed in the page footer ("v: rolling (current)") and is # interpolated into _templates/llms.txt.j2 ("This documentation covers # {{ release }}."). Pin this to a value that names what the rolling -# docs actually serve, not a stale LTS codename. +# docs actually serve, not a stale LTS codename. The "(current)" suffix +# is release-channel terminology for the current rolling release; it is +# unrelated to the former branch name. release = u'rolling (current)' # -- General configuration --------------------------------------------------- @@ -136,9 +138,9 @@ html_baseurl = 'https://docs.vyos.io/en/rolling/' _rtd_version_type = os.environ.get('READTHEDOCS_VERSION_TYPE', '') _github_version = ( - os.environ.get('READTHEDOCS_GIT_COMMIT_HASH', 'current') + os.environ.get('READTHEDOCS_GIT_COMMIT_HASH', 'rolling') if _rtd_version_type == 'external' - else os.environ.get('READTHEDOCS_GIT_IDENTIFIER', 'current') + else os.environ.get('READTHEDOCS_GIT_IDENTIFIER', 'rolling') ) html_context = { diff --git a/scripts/doc-linter.py b/scripts/doc-linter.py new file mode 100644 index 00000000..3dc7c2fc --- /dev/null +++ b/scripts/doc-linter.py @@ -0,0 +1,177 @@ +import os +import re +import ipaddress +import sys +import ast + +IPV4SEG = r'(?:25[0-5]|(?:2[0-4]|1{0,1}[0-9]){0,1}[0-9])' +IPV4ADDR = r'\b(?:(?:' + IPV4SEG + r'\.){3,3}' + IPV4SEG + r')\b' +IPV6SEG = r'(?:(?:[0-9a-fA-F]){1,4})' +IPV6GROUPS = ( + r'(?:' + IPV6SEG + r':){7,7}' + IPV6SEG, # 1:2:3:4:5:6:7:8 + r'(?:\s' + IPV6SEG + r':){1,7}:', # 1:: 1:2:3:4:5:6:7:: + r'(?:' + IPV6SEG + r':){1,6}:' + IPV6SEG, # 1::8 1:2:3:4:5:6::8 1:2:3:4:5:6::8 + r'(?:' + IPV6SEG + r':){1,5}(?::' + IPV6SEG + r'){1,2}', # 1::7:8 1:2:3:4:5::7:8 1:2:3:4:5::8 + r'(?:' + IPV6SEG + r':){1,4}(?::' + IPV6SEG + r'){1,3}', # 1::6:7:8 1:2:3:4::6:7:8 1:2:3:4::8 + r'(?:' + IPV6SEG + r':){1,3}(?::' + IPV6SEG + r'){1,4}', # 1::5:6:7:8 1:2:3::5:6:7:8 1:2:3::8 + r'(?:' + IPV6SEG + r':){1,2}(?::' + IPV6SEG + r'){1,5}', # 1::4:5:6:7:8 1:2::4:5:6:7:8 1:2::8 + IPV6SEG + r':(?:(?::' + IPV6SEG + r'){1,6})', # 1::3:4:5:6:7:8 1::3:4:5:6:7:8 1::8 + r':(?:(?::' + IPV6SEG + r'){1,7}|:)', # ::2:3:4:5:6:7:8 ::2:3:4:5:6:7:8 ::8 :: + r'fe80:(?::' + IPV6SEG + r'){0,4}%[0-9a-zA-Z]{1,}', # fe80::7:8%eth0 fe80::7:8%1 (link-local IPv6 addresses with zone index) + r'::(?:ffff(?::0{1,4}){0,1}:){0,1}[^\s:]' + IPV4ADDR, # ::255.255.255.255 ::ffff:255.255.255.255 ::ffff:0:255.255.255.255 (IPv4-mapped IPv6 addresses and IPv4-translated addresses) + r'(?:' + IPV6SEG + r':){1,4}:[^\s:]' + IPV4ADDR, # 2001:db8:3:4::192.0.2.33 64:ff9b::192.0.2.33 (IPv4-Embedded IPv6 Address) +) +IPV6ADDR = '|'.join(['(?:{})'.format(g) for g in IPV6GROUPS[::-1]]) # Reverse rows for greedy match + +MAC = r'([0-9A-F]{2}[:-]){5}([0-9A-F]{2})' + +NUMBER = r"([\s']\d+[\s'])" + + +def lint_mac(cnt, line): + mac = re.search(MAC, line, re.I) + if mac is not None: + mac = mac.group() + u_mac = re.search(r'((00)[:-](53)([:-][0-9A-F]{2}){4})', mac, re.I) + m_mac = re.search(r'((90)[:-](10)([:-][0-9A-F]{2}){4})', mac, re.I) + if u_mac is None and m_mac is None: + return (f"Use MAC reserved for Documentation (RFC7042): {mac}", cnt, 'error') + + +def lint_ipv4(cnt, line): + ip = re.search(IPV4ADDR, line, re.I) + if ip is not None: + ip = ipaddress.ip_address(ip.group().strip(' ')) + # https://docs.python.org/3/library/ipaddress.html#ipaddress.IPv4Address.is_private + if ip.is_private: + return None + if ip.is_multicast: + return None + if ip.is_global is False: + return None + return (f"Use IPv4 reserved for Documentation (RFC 5737) or private Space: {ip}", cnt, 'error') + + +def lint_ipv6(cnt, line): + ip = re.search(IPV6ADDR, line, re.I) + if ip is not None: + ip = ipaddress.ip_address(ip.group().strip(' ')) + if ip.is_private: + return None + if ip.is_multicast: + return None + if ip.is_global is False: + return None + return (f"Use IPv6 reserved for Documentation (RFC 3849) or private Space: {ip}", cnt, 'error') + + +def lint_AS(cnt, line): + number = re.search(NUMBER, line, re.I) + if number: + pass + # find a way to detect AS numbers + + +def lint_linelen(cnt, line): + line = line.rstrip() + if len(line) > 80: + return (f"Line too long: len={len(line)}", cnt, 'warning') + +def handle_file_action(filepath): + errors = [] + try: + with open(filepath) as fp: + line = fp.readline() + cnt = 1 + test_line_lenght = True + start_vyoslinter = True + indentation = 0 + while line: + # search for ignore linter comments in lines + if ".. stop_vyoslinter" in line: + start_vyoslinter = False + if ".. start_vyoslinter" in line: + start_vyoslinter = True + if start_vyoslinter: + # ignore every '.. code-block::' for line lenght + # rst code-block have its own style in html the format in rst + # and the build page must be the same + if test_line_lenght is False: + if len(line) > indentation: + #print(f"'{line}'") + #print(indentation) + if line[indentation].isspace() is False: + test_line_lenght = True + + if ".. code-block::" in line: + test_line_lenght = False + indentation = 0 + for i in line: + if i.isspace(): + indentation = indentation + 1 + else: + break + + err_mac = lint_mac(cnt, line.strip()) + # disable mac detection for the moment, too many false positives + err_mac = None + err_ip4 = lint_ipv4(cnt, line.strip()) + err_ip6 = lint_ipv6(cnt, line.strip()) + if test_line_lenght: + err_len = lint_linelen(cnt, line) + else: + err_len = None + if err_mac: + errors.append(err_mac) + if err_ip4: + errors.append(err_ip4) + if err_ip6: + errors.append(err_ip6) + if err_len: + errors.append(err_len) + + line = fp.readline() + cnt += 1 + + # ensure linter was not stop on top and forgot to tun on again + if start_vyoslinter == False: + errors.append((f"Don't forgett to turn linter back on", cnt, 'error')) + finally: + fp.close() + + if len(errors) > 0: + ''' + "::{$type} file={$filename},line={$line},col=$column::{$log}" + ''' + print(f"File: {filepath}") + for error in errors: + print(f"::{error[2]} file={filepath},line={error[1]}::{error[0]}") + print('') + return False + + +def main(): + bool_error = True + print('start') + try: + files = ast.literal_eval(sys.argv[1]) + for file in files: + if file[-4:] in [".rst", ".txt"] and "_build" not in file: + if handle_file_action(file) is False: + bool_error = False + except Exception as e: + for root, dirs, files in os.walk("docs"): + path = root.split(os.sep) + for file in files: + if file[-4:] in [".rst", ".txt"] and "_build" not in path: + fpath = '/'.join(path) + filepath = f"{fpath}/{file}" + if handle_file_action(filepath) is False: + bool_error = False + + return bool_error + + +if __name__ == "__main__": + if main() == False: + exit(1) |
