Skip to content

Commit fde7ea2

Browse files
authored
added linter for code block line length and gen_numbering (#70)
## Improvements: - Code Blocks shall only have a max line length of 100 characters, because otherwise they are not completely included in the syllabus pdf. This is now automatically checked during build and build will fail if a line is longer. - The Python script to generate the chapter numbers to the headings, has been improved, to return with an exit code != 0 in case of a fix. This will now be used as check in the build and also available as pre-commit hook to the maintainers. --------- Signed-off-by: René <snooz@posteo.de>
1 parent 1e9a18d commit fde7ea2

24 files changed

+370
-178
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ results
66
.vscode
77
.devcontainer
88
.venv
9+
Syllabus.pdf

.pre-commit-config.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
repos:
2+
- repo: local
3+
hooks:
4+
- id: gen-numbering
5+
name: Ensure numbering, TOC, and links are in sync
6+
entry: python tools/gen_numbering.py
7+
language: system
8+
pass_filenames: false
9+
always_run: true

CONTRIBUTION.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Contributing
2+
3+
Thanks for helping us improve the RFCP syllabus! The notes below explain how to bootstrap the tooling, run Docusaurus locally, and keep the docs consistent.
4+
5+
## 1. Install prerequisites
6+
- Node.js 20+ (needed for Docusaurus and Playwright)
7+
- Python 3.11+ (the repo bootstrap script creates a virtual environment for you)
8+
- npm (ships with Node)
9+
10+
## 2. Project setup
11+
1. From the repository root, bootstrap both the Python and JavaScript toolchains:
12+
```bash
13+
python bootstrap.py
14+
```
15+
The script upgrades `pip`, installs `pre-commit`, and asks whether you want to pull in the optional PDF toolchain (Robot Framework + Browser batteries from `requirements.txt`, plus Chromium via `rfbrowser install chromium`). Answer "y" only if you plan to regenerate the PDF. Regardless of that choice, the script installs the pre-commit hook and then runs `npm install` inside `website/` so Docusaurus has its dependencies.
16+
2. Start Docusaurus in development mode while you edit:
17+
```bash
18+
cd website # if not already there
19+
npm run start
20+
```
21+
22+
## 3. Quality checks before committing
23+
- `pre-commit` automatically runs `python tools/gen_numbering.py` on every commit. If headings, learning-objective numbering, or internal links change the hook updates the files, prints a diff, and blocks the commit until you stage the fixes.
24+
- All fenced code blocks must stay within 100 characters per line. Run `npm run build` in the `website/` directory to check code block line lengths (or let CI run it).
25+
- Docusaurus validates internal links during builds. Run `npm run build` before submitting a PR to catch broken anchors or missing pages early.
26+
27+
## 4. Generating the syllabus PDF
28+
To rebuild the syllabus PDF from the Robot Framework assets, run the Robot Framework suite from the repo root:
29+
30+
1. Activate the created virtual environment so local commands use the same interpreters as the hooks:
31+
- macOS/Linux: `source .venv/bin/activate`
32+
- Windows (PowerShell): `.venv\Scripts\Activate.ps1`
33+
34+
2. Run the suite to generate the PDF:
35+
```bash
36+
robot -d results tools/gen_pdf.robot
37+
```
38+
The PDF and execution logs land inside `results/`.
39+
40+
Following the steps above keeps your local environment consistent with CI and ensures contributions land cleanly. Happy writing!

LOs.csv

Lines changed: 136 additions & 136 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,16 @@
4646
- LO-2.1.1 (K1) Recall the conditions and requirements for a file to be considered a Suite file
4747
- [`2.1.2 Sections and Their Artifacts`](website/docs/chapter-02/01_suitefile.md#212-sections-and-their-artifacts)
4848
- LO-2.1.2 (K1) Recall the available sections in a suite file and their purpose.
49-
- [`2.1.2.1 Introduction in `*** Settings ***` Section`](website/docs/chapter-02/01_suitefile.md#2121-introduction-to--settings--section)
49+
- [`2.1.2.1 Introduction to `*** Settings ***` Section`](website/docs/chapter-02/01_suitefile.md#2121-introduction-to--settings--section)
5050
- LO-2.1.2.1-1 (K1) Recall the available settings in a suite file.
5151
- LO-2.1.2.1-2 (K2) Understand the concepts of suite settings and how to define them.
52-
- [`2.1.2.2 Introduction in `*** Variables ***` Section`](website/docs/chapter-02/01_suitefile.md#2122-introduction-to--variables--section)
52+
- [`2.1.2.2 Introduction to `*** Variables ***` Section`](website/docs/chapter-02/01_suitefile.md#2122-introduction-to--variables--section)
5353
- LO-2.1.2.2 (K1) Recall the purpose of the `*** Variables ***` section.
54-
- [`2.1.2.3 Introduction in `*** Test Cases ***` or `*** Tasks ***` Section`](website/docs/chapter-02/01_suitefile.md#2123-introduction-to--test-cases--or--tasks--section)
54+
- [`2.1.2.3 Introduction to `*** Test Cases ***` or `*** Tasks ***` Section`](website/docs/chapter-02/01_suitefile.md#2123-introduction-to--test-cases--or--tasks--section)
5555
- LO-2.1.2.3 (K2) Understand the purpose of the `*** Test Cases ***` or `*** Tasks ***` section.
56-
- [`2.1.2.4 Introduction in `*** Keywords ***` Section`](website/docs/chapter-02/01_suitefile.md#2124-introduction-to--keywords--section)
56+
- [`2.1.2.4 Introduction to `*** Keywords ***` Section`](website/docs/chapter-02/01_suitefile.md#2124-introduction-to--keywords--section)
5757
- LO-2.1.2.4 (K2) Understand the purpose and limitations of the `*** Keywords ***` section.
58-
- [`2.1.2.5 Introduction in `*** Comments ***` Section`](website/docs/chapter-02/01_suitefile.md#2125-introduction-to--comments--section)
58+
- [`2.1.2.5 Introduction to `*** Comments ***` Section`](website/docs/chapter-02/01_suitefile.md#2125-introduction-to--comments--section)
5959
- [`2.2 Basic Suite File Syntax`](website/docs/chapter-02/02_suitefile_syntax.md)
6060
- LO-2.2 (K2) Understand the basic syntax of test cases and tasks.
6161
- [`2.2.1 Separation and Indentation`](website/docs/chapter-02/02_suitefile_syntax.md#221-separation-and-indentation)
@@ -222,7 +222,7 @@
222222
- [`4.2.3 Keyword Teardown`](website/docs/chapter-04/02_teardowns.md#423-keyword-teardown)
223223
- LO-4.2.3 (K1) Recall key characteristics, benefits, and syntax of Keyword Teardown
224224
- [`4.3 Initialization Files`](website/docs/chapter-04/03_init_files.md)
225-
- LO-4.3 (K1) Recall how to define an Initialization Files and its purpose
225+
- LO-4.3 (K1) Recall how to define Initialization Files and its purpose
226226
- [`4.3.1 Purpose of Initialization Files`](website/docs/chapter-04/03_init_files.md#431-purpose-of-initialization-files)
227227
- [`4.3.2 Suite Setup and Suite Teardown of Initialization Files`](website/docs/chapter-04/03_init_files.md#432-suite-setup-and-suite-teardown-of-initialization-files)
228228
- LO-4.3.2 (K2) Understand the execution order of Suite Setup and Suite Teardown in Initialization Files and their sub-suites and tests|tasks
@@ -282,7 +282,7 @@
282282
- [`5.2.1 IF Statements`](website/docs/chapter-05/02_control_structures.md#521-if-statements)
283283
- LO-5.2.1 (K2) Understand the purpose and basic concept of IF-Statements
284284
- [`5.2.1.1 Basic IF Syntax`](website/docs/chapter-05/02_control_structures.md#5211-basic-if-syntax)
285-
- [`5.2.2 IF/ELSE IF/ELSE Structure`](website/docs/chapter-05/02_control_structures.md#522-ifelse-ifelse-structure)
285+
- [`5.2.2 IF/ELSE Structure`](website/docs/chapter-05/02_control_structures.md#522-ifelse-structure)
286286
- [`5.2.3 Inline IF Statement`](website/docs/chapter-05/02_control_structures.md#523-inline-if-statement)
287287
- [`5.2.4 FOR Loops`](website/docs/chapter-05/02_control_structures.md#524-for-loops)
288288
- LO-5.2.4 (K2) Understand the purpose and basic concept of FOR Loops

Syllabus.pdf

-8.49 KB
Binary file not shown.

bootstrap.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import platform
2+
import subprocess
3+
from pathlib import Path
4+
from venv import EnvBuilder
5+
6+
7+
class Colors:
8+
"""ANSI color codes"""
9+
10+
BLACK = "\033[0;30m"
11+
RED = "\033[0;31m"
12+
GREEN = "\033[0;32m"
13+
BROWN = "\033[0;33m"
14+
BLUE = "\033[0;34m"
15+
PURPLE = "\033[0;35m"
16+
CYAN = "\033[0;36m"
17+
LIGHT_GRAY = "\033[0;37m"
18+
DARK_GRAY = "\033[1;30m"
19+
LIGHT_RED = "\033[1;31m"
20+
LIGHT_GREEN = "\033[1;32m"
21+
YELLOW = "\033[1;33m"
22+
LIGHT_BLUE = "\033[1;34m"
23+
LIGHT_PURPLE = "\033[1;35m"
24+
LIGHT_CYAN = "\033[1;36m"
25+
LIGHT_WHITE = "\033[1;37m"
26+
BOLD = "\033[1m"
27+
FAINT = "\033[2m"
28+
ITALIC = "\033[3m"
29+
UNDERLINE = "\033[4m"
30+
BLINK = "\033[5m"
31+
NEGATIVE = "\033[7m"
32+
CROSSED = "\033[9m"
33+
END = "\033[0m"
34+
# cancel SGR codes if we don't write to a terminal
35+
if not __import__("sys").stdout.isatty():
36+
for attr in dir():
37+
if not attr.startswith("_"):
38+
if isinstance(getattr(__class__, attr), str):
39+
setattr(__class__, attr, "")
40+
elif __import__("platform").system() == "Windows":
41+
kernel32 = __import__("ctypes").windll.kernel32
42+
kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
43+
del kernel32
44+
45+
46+
venv_dir = Path() / ".venv"
47+
requirements_file = Path("requirements.txt")
48+
website_dir = Path("website")
49+
if not platform.platform().startswith("Windows"):
50+
venv_bin = venv_dir / "bin"
51+
venv_python = venv_bin / "python"
52+
venv_pre_commit = venv_bin / "pre-commit"
53+
else:
54+
venv_bin = venv_dir / "Scripts"
55+
venv_python = venv_bin / "python.exe"
56+
venv_pre_commit = venv_bin / "pre-commit.exe"
57+
58+
if not venv_dir.exists():
59+
print(f"Creating virtualenv in {venv_dir}")
60+
EnvBuilder(with_pip=True).create(venv_dir)
61+
62+
subprocess.run([venv_python, "-m", "pip", "install", "-U", "pip"], check=True)
63+
subprocess.run([venv_python, "-m", "pip", "install", "-U", "pre-commit"], check=True)
64+
install_pdf_tooling = False
65+
if requirements_file.exists():
66+
answer = input(f"{Colors.GREEN}Install Robot Framework + browser batteries for PDF generation?{Colors.END} {Colors.YELLOW}[y/N]: {Colors.END}").strip().lower()
67+
install_pdf_tooling = answer in {"y", "yes"}
68+
if install_pdf_tooling:
69+
subprocess.run([venv_python, "-m", "pip", "install", "-r", str(requirements_file)], check=True)
70+
subprocess.run([venv_python, "-m", "Browser.entry", "install", "chromium", "--with-deps"], check=True)
71+
72+
subprocess.run([venv_pre_commit, "install"], check=True)
73+
74+
if website_dir.exists():
75+
npm_cmd = ["npm", "install"]
76+
print(f"Running {' '.join(npm_cmd)} in {website_dir}")
77+
subprocess.run(npm_cmd, cwd=website_dir, check=True)
78+
79+
activate_script = (
80+
"source .venv/bin/activate"
81+
if not platform.platform().startswith("Windows")
82+
else ".venv\\Scripts\\activate"
83+
)
84+
85+
print(f"\n\nVirtualenv '{Colors.GREEN}{venv_dir}{Colors.END}' is ready and up-to-date.")
86+
print(f"Run '{Colors.GREEN}{activate_script}{Colors.END}' to activate the virtualenv.")

requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
robotframework-browser-batteries
2+
pypdf

tools/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

tools/gen_pdf.robot

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ Library Process
55

66
*** Variables ***
77
@{EXCLUDE_FROM_PDF} Example Questions
8+
${BROWSER} chromium
9+
${HEADLESS} ${True}
810

911

1012
*** Test Cases ***
@@ -22,7 +24,11 @@ Build Docusaurus
2224
Process.Start Process npm run serve alias=docusaurus cwd=website
2325

2426
Open Syllabus
25-
New Browser chromium headless=False
27+
IF $BROWSER.lower() in ['chrome', 'msedge']
28+
New Browser chromium channel=${BROWSER} headless=${HEADLESS}
29+
ELSE
30+
New Browser chromium headless=${HEADLESS}
31+
END
2632
New Page http://localhost:3000/robotframework-RFCP-syllabus/docs/overview
2733
${dark} Get Element States
2834
... button[aria-label="Switch between dark and light mode (currently dark mode)"]

0 commit comments

Comments
 (0)