Skip to content

Commit 8f7f03d

Browse files
authored
Merge pull request #65 from waterlinked/add-link-checker-2
Add link checker 2
2 parents 4d2839a + cb4c475 commit 8f7f03d

38 files changed

+284
-87
lines changed

.github/workflows/build.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Deploy Water Linked Docs
1+
name: Build Docs
22

33
on:
44
push:
@@ -26,6 +26,7 @@ jobs:
2626
- name: Build docs
2727
run: |
2828
mkdocs build --strict
29+
./check-links.sh
2930
3031
# Only deploy on master
3132
- name: Deploy docs

README.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,33 @@ This is done by creating a pull request.
1414
Make sure you have Python3 installed.
1515

1616
```
17-
git clone https://github.com/waterlinked/waterlinked.github.io
18-
cd waterlinked.github.io
17+
git clone https://github.com/waterlinked/docs.git
18+
cd docs
1919
2020
python -m venv venv
2121
source venv/bin/activate (Linux)
2222
venv\Scripts\activate.bat (Windows)
2323
pip install -r requirements.txt
2424
25-
mkdocs serve
25+
./install-hooks.sh # (Optional) To automatically check links on git push
2626
```
2727

2828
2. Make changes using your favorite editor
2929

3030
3. Test them
3131

32+
```
33+
mkdocs serve # Allow you to view the changes on your browser
34+
```
3235
* Fire up your browser and go to localhost:8000
3336

37+
Verify links are valid:
38+
39+
```
40+
./check-links.sh
41+
```
42+
3443
## Deploy changes to server
35-
After the changes have been tested and they work, push the changes to the master branch and Github will build the website and publish it on https://docs.waterlinked.com.
44+
After the changes have been tested and they work, push the changes to a branch, and make a merge request. The documentation site will built automatically and links will be verified.
3645

46+
Once the pull request is merged the documentation will be automatically built and published to https://docs.waterlinked.com.

check-internal-links.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#!/usr/bin/env python3
2+
import re
3+
import sys
4+
from pathlib import Path
5+
6+
# Regex for Markdown links: [text](target)
7+
LINK_RE = re.compile(r"\[([^\]]+)\]\(([^)]+)\)")
8+
9+
def slugify_heading(text: str) -> str:
10+
"""Convert Markdown heading text to MkDocs anchor format."""
11+
slug = text.strip().lower()
12+
slug = re.sub(r"[^\w\s-]", "", slug) # remove punctuation
13+
slug = re.sub(r"\s+", "-", slug) # spaces -> dashes
14+
return slug
15+
16+
17+
def extract_headings(md_file: Path):
18+
"""Return a set of anchor slugs from headings in md files."""
19+
headings = set()
20+
if not md_file.is_file():
21+
return headings
22+
for line in md_file.read_text(encoding="utf-8").splitlines():
23+
if line.startswith("#"):
24+
heading_text = line.lstrip("#").strip()
25+
headings.add(slugify_heading(heading_text))
26+
return headings
27+
28+
def check_md_file(md_file: Path):
29+
"""Check links in md files."""
30+
errors = []
31+
if not md_file.is_file(): # <-- add this check
32+
return errors
33+
34+
for line_number, line in enumerate(md_file.read_text(encoding="utf-8").splitlines(), start=1):
35+
36+
stripped = line.strip()
37+
# Skip HTML and python comments
38+
if stripped.startswith("#"):
39+
continue
40+
elif stripped.startswith("<!--") and stripped.endswith("-->"):
41+
continue
42+
43+
for match in LINK_RE.finditer(line):
44+
text, target = match.groups()
45+
46+
# Skip external links
47+
if target.startswith(("http://", "https://", "mailto:")):
48+
continue
49+
50+
# Remove leading slash for site-root relative links
51+
elif target.startswith("/"):
52+
target = target[1:]
53+
54+
# Split anchor from file
55+
if "#" in target:
56+
if target.startswith("#"):
57+
file_part, anchor = md_file, target[1:]
58+
else:
59+
file_part, anchor = target.split("#", 1)
60+
else:
61+
file_part, anchor = target, None
62+
63+
# Resolve relative path'
64+
target_path = Path(file_part) if isinstance(file_part, Path) else Path(file_part) #Make sure it's a Path object
65+
target_file = (md_file.parent / target_path).resolve()
66+
67+
if not target_file.exists() and not target_file.suffix:
68+
target_file = (md_file.parent / (target_path.name + ".md")).resolve()
69+
if not target_file.exists():
70+
errors.append(f"{md_file}:{line_number}: File not found -> {target}")
71+
continue
72+
73+
if target_file.is_file() and anchor:
74+
headings = extract_headings(target_file)
75+
if anchor not in headings:
76+
errors.append(f"{md_file}:{line_number}: Anchor not found -> {target}")
77+
78+
if target_file.is_dir():
79+
continue
80+
81+
return errors
82+
83+
84+
def main(md_dir):
85+
md_dir = Path(md_dir)
86+
all_errors = []
87+
for md_file in md_dir.rglob("*.md"):
88+
all_errors.extend(check_md_file(md_file))
89+
90+
if all_errors:
91+
print(f'Found {len(all_errors)} internal link errors in md files:')
92+
for e in all_errors:
93+
print(e)
94+
return 1
95+
print("No internal link errors found.")
96+
return 0
97+
98+
if __name__ == "__main__":
99+
exit(main(sys.argv[1]))

check-links.sh

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#!/bin/bash
2+
# Pre-push hook: Custom linkchecker for local links, Build MkDocs locally and run LinkChecker on external links
3+
4+
# Exit on error
5+
set -e
6+
7+
# === Paths ===
8+
PROJECT_ROOT="$(git rev-parse --show-toplevel)"
9+
VENV_BIN="$PROJECT_ROOT/venv/bin"
10+
TMP_BUILD_DIR="$PROJECT_ROOT/.tmp-mkdocs-build"
11+
DOCS_DIR="$PROJECT_ROOT/docs"
12+
13+
# Activate venv if not already activated
14+
if [ -f "$VENV_BIN/activate" ]; then
15+
. "$VENV_BIN/activate"
16+
fi
17+
18+
#Checking internal links in markdown files only
19+
echo "Checking internal links in markdown files..."
20+
21+
set +e
22+
#Using custom python script for internal link checking:
23+
python "$PROJECT_ROOT/check-internal-links.py" "$DOCS_DIR"
24+
RESULT_INTERNAL=$?
25+
26+
set -e
27+
28+
if [ $RESULT_INTERNAL -ne 0 ]; then
29+
echo "Internal linkcheck failed! Please fix Markdown links before pushing."
30+
exit 1
31+
fi
32+
33+
echo "Internal linkcheck passed!"
34+
35+
# Check if linkchecker exists
36+
if ! command -v linkchecker >/dev/null 2>&1; then
37+
echo "Error: linkchecker executable not found in PATH"
38+
exit 1
39+
fi
40+
41+
# Remove old temp build if it exists
42+
rm -rf "$TMP_BUILD_DIR"
43+
44+
# Build MkDocs into temporary directory
45+
echo "Building MkDocs locally into $TMP_BUILD_DIR..."
46+
python -m mkdocs build -d "$TMP_BUILD_DIR"
47+
48+
echo "Running LinkChecker on external links against local build..."
49+
set +e
50+
# Only report broken links
51+
linkchecker "file://$TMP_BUILD_DIR/index.html" \
52+
--no-status \
53+
--check-extern \
54+
--recursion-level=2 \
55+
--ignore-url='sitemap\.xml\.gz' \
56+
--ignore-url='https://github.com/.*/edit/' \
57+
--ignore-url='https://www.youtube.com/' \
58+
--ignore-url='.*/assets/.*' \
59+
--ignore-url='.*/images/.*'
60+
61+
62+
RESULT_EXTERNAL=$?
63+
set -e
64+
65+
# Clean up
66+
rm -rf "$TMP_BUILD_DIR"
67+
68+
if [ $RESULT_EXTERNAL -ne 0 ]; then
69+
echo "External linkcheck failed! Please fix broken links before pushing."
70+
exit 1
71+
fi
72+
73+
echo "External linkcheck passed!"
74+
exit 0

docs/dvl/axes.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ By default, the body frame and vehicle frame is the same and align with the DVL'
5252

5353
The DVL can be mounted at an angle to the forward direction of a vehicle to which it is attached.
5454

55-
To be precise, the clockwise angle θ in degrees around the Z axis (i.e. in the X-Y plane) from the forward axis of the vehicle to the forward axis of the DVL can be entered as a 'mounting rotation offset' in the [GUI](../gui/configuration), or via the TCP or serial [protocols](../dvl-protocol/).
55+
To be precise, the clockwise angle θ in degrees around the Z axis (i.e. in the X-Y plane) from the forward axis of the vehicle to the forward axis of the DVL can be entered as a 'mounting rotation offset' in the [GUI](../dvl/gui/configuration.md), or via the TCP or serial [protocols](../dvl-protocol/).
5656

5757
The DVL will then output data in the vehicle frame obtained by rotating the [DVL body frame](#body-frame) anti-clockwise around the Z-axis by θ degrees: the X-axis of the velocities outputted by the DVL will be aligned with the forward axis of the vehicle, and, at time zero, the X-axis of the DVL's [frame](../dead-reckoning#frame) for dead reckoning will be aligned with the forward axis of the vehicle.
5858

docs/dvl/bluerov-integration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ You will also need:
4747
* Utility knife
4848
* Wire stripping tool
4949
* Zip ties
50-
* 4 x 30 cm (12") wires (options discussed under [Connect DVL-A50 and BlueROV2](#connect-dvl-a50-and-bluerov2)). If possible, use 2 x red and 2 x black wires.
50+
* 4 x 30 cm (12") wires (options discussed under [Connect DVL-A50 and BlueROV2](#attach-dvl-a50-to-bluerov2)). If possible, use 2 x red and 2 x black wires.
5151
* 15 cm (6") ethernet cable
5252

5353
## Preparation

docs/dvl/dead-reckoning.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ The DVL-A50 and DVL-A125 run a dead reckoning algorithm which estimates the orie
44

55
## Starting dead reckoning
66

7-
1. Calibrate the gyroscope by pressing *More -> Calibrate gyro* in the [GUI](../gui/dashboard) whilst the DVL is stationary.
8-
2. Click the 'Reset' button ![](../img/dvl_gui_icon_reset.png) in the [GUI](../gui/dashboard), or send a reset command over the TCP or serial [protocol](../dvl-protocol).
7+
1. Calibrate the gyroscope by pressing *More -> Calibrate gyro* in the [GUI](../dvl/gui/dashboard.md) whilst the DVL is stationary.
8+
2. Click the 'Reset' button ![](../img/dvl_gui_icon_reset.png) in the [GUI](../dvl/gui/dashboard.md), or send a reset command over the TCP or serial [protocol](../dvl-protocol).
99

1010
Failure to perform gyro calibration will result in less accurate dead reckoning.
1111

@@ -22,11 +22,11 @@ When the DVL fails in its determination of velocity, speed and position are pred
2222
!!! note
2323
When the DVL is powered on in the air, its position will drift significantly. This should be ignored, and dead reckoning should be [started](#starting-dead-reckoning) in water when ready.
2424

25-
The position of the DVL can be viewed in the GUI [dashboard](../gui/dashboard/) or be fetched by [API](../dvl-protocol/#dead-reckoning-report).
25+
The position of the DVL can be viewed in the GUI [dashboard](../dvl/gui/dashboard.md) or be fetched by [API](../dvl-protocol/#dead-reckoning-report).
2626

2727
## Orientation
2828

29-
The calculation of the orientation of the DVL is based upon the accelerometer and gyroscope measurements of its IMU. The orientation is represented by roll, pitch, and yaw angles, and can be viewed in the GUI [dashboard](../gui/dashboard/) or be fetched by [API](../dvl-protocol/#dead-reckoning-report).
29+
The calculation of the orientation of the DVL is based upon the accelerometer and gyroscope measurements of its IMU. The orientation is represented by roll, pitch, and yaw angles, and can be viewed in the GUI [dashboard](../dvl/gui/dashboard.md) or be fetched by [API](../dvl-protocol/#dead-reckoning-report).
3030

3131
- Roll is a rotation around the X axis of the DVL
3232
- Pitch is a rotation around the Y axis of the DVL

docs/dvl/dvl-protocol.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -538,7 +538,7 @@ The new configuration will not be returned in the response, but can be obtained
538538

539539
### Velocity report, old format (wrx) [Deprecated]
540540

541-
Same purpose as the [velocity report](#velocity-report), but in an older format:
541+
Same purpose as the [velocity report](#velocity-report-wrz), but in an older format:
542542

543543
`wrx,`*[time],[vx],[vy],[vz],[fom],[altitude],[valid],[status]*
544544

@@ -571,7 +571,7 @@ wrx,1164.94,0.000,0.000,0.000,2.707,-1.00,n,1*39
571571

572572
### Transducer report, old format (wrt) [Deprecated]
573573

574-
Same purpose as the [transducer report](#transducer-report), but in an older format, and combining the data of all four transducers:
574+
Same purpose as the [transducer report](#transducer-report-wru), but in an older format, and combining the data of all four transducers:
575575

576576
`wrt,`*[dist_1],[dist_2],[dist_3],[dist_4]*
577577

@@ -605,7 +605,7 @@ Checksum is formatted as a hexadecimal number using 2 lower-case characters (ex:
605605
Compatible implementations:
606606

607607
* Python 3: [crcmod](https://pypi.org/project/crcmod/) `crcmod.predefined.mkPredefinedCrcFun("crc-8")`
608-
* Golang: [github.com/sigurn/crc8](github.com/sigurn/crc8) `crc8.MakeTable(crc8.CRC8)`
608+
* Golang: [github.com/sigurn/crc8](https://github.com/sigurn/crc8) `crc8.MakeTable(crc8.CRC8)`
609609

610610
Example for how to verify checksum using Python 3 and [crcmod](https://pypi.org/project/crcmod/):
611611

docs/dvl/dvl-troubleshooting.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@ Below is a small list to check if your DVL is not performing as expected or you
66

77
In the Support Portal please describe the test environment or test setup you have used and how the DVL is mounted. If possible, open the DVL GUI and go to Diagnostic Log in the left menu. Press the Create Diagnostic Report button. This will automatically record a log file that you can download to your computer. Please add the [Diagnostic reports](gui/diagnostic-report.md) and pictures of the test setup in the support portal. This will help our support team greatly!
88

9-
If the troubleshooting guide do not help, please check out [FAQ DVL](faq.md#faq-dvl)!
9+
If the troubleshooting guide do not help, please check out [FAQ DVL](../dvl/faq.md)!
1010

1111
### Check if DVL is powered
1212
1. Put the DVL under water and power it on with an adequate power supply.
1313
* Does the DVL pull any current? Expected current consumption: Approximate average 260mA at 18V
1414
* Does the LED turn on?
1515

1616
### LED
17-
1. For LED status, see [LED Signals](../interfaces#LED-Signals).
17+
1. For LED status, see [LED Signals](../dvl/interfaces.md#led-signals).
1818

1919
### Wiring
2020
1. Check that you have wired your DVL correctly, see [Wiring interface](../interfaces#wiring-interface).

docs/dvl/gui/dashboard.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# Dashboard
22

3-
Both the DVL-A50 and the DVL-A125 have a web-based GUI. In your favorite web browser, simply navigate to the DVL's [IP address](../../networking).
3+
Both the DVL-A50 and the DVL-A125 have a web-based GUI. In your favorite web browser, simply navigate to the DVL's [IP address](../networking.md).
44

5-
The default page is a dashboard which provides a summary and visualisation of both the velocity and [dead reckoning](../../dead-reckoning) data outputted by the DVL, as well as an indication of current status.
5+
The default page is a dashboard which provides a summary and visualisation of both the velocity and [dead reckoning](../dead-reckoning.md) data outputted by the DVL, as well as an indication of current status.
66

77
## Screenshot
88

0 commit comments

Comments
 (0)