jamesread 1 неделя назад
Родитель
Сommit
9e60d3b9e4

+ 64 - 24
docs/modules/ROOT/check_chevron_links.py

@@ -1,40 +1,80 @@
 #!/usr/bin/env python3
 
-import glob
+# Find <<anchor>> links whose target is not defined on the assembled page.
+# include::partial$... directives are expanded so chevrons inside partials are
+# checked against every page that includes them.
+
+from pathlib import Path
 import re
+import sys
+
+ROOT = Path(__file__).resolve().parent
+PAGES = ROOT / "pages"
+PARTIALS = ROOT / "partials"
+PARTIALS_ROOT = PARTIALS.resolve()
+
+INCLUDE_RE = re.compile(r"include::partial\$([^\[\]]+)\[([^\]]*)\]")
+CHEVRON_RE = re.compile(r"<<([^,>]+)(?:,[^>]*)?>>")
+
+
+def expand_partials(content, stack):
+    def replace(match):
+        rel = match.group(1)
+        partial_path = (PARTIALS / rel).resolve()
+
+        if not partial_path.is_relative_to(PARTIALS_ROOT):
+            return ""
+
+        if not partial_path.is_file() or partial_path in stack:
+            return ""
+
+        included = partial_path.read_text()
+        return expand_partials(included, stack | {partial_path})
+
+    return INCLUDE_RE.sub(replace, content)
+
+
+def count_anchors(content, anchor_id):
+    escaped = re.escape(anchor_id)
+    pattern = rf"\[(?:#|\[){escaped}(?=[,\]])"
+    return len(re.findall(pattern, content))
+
+
+def find_unresolved(content):
+    unresolved = []
+
+    for match in CHEVRON_RE.finditer(content):
+        anchor_id = match.group(1).strip()
 
-nav_file = open('nav.adoc', 'r')
-nav_string = nav_file.read()
+        if count_anchors(content, anchor_id) == 1:
+            continue
 
-adoc_files = glob.glob('pages/**/*.adoc', recursive=True)
+        if anchor_id not in unresolved:
+            unresolved.append(anchor_id)
 
-filelist = dict()
+    return unresolved
 
-for file in adoc_files:
-    with open(file, 'r') as handle:
-        content = handle.read()
 
-        matches = re.findall(r'<<(.*?),?([\w\- ]+)>>', content)
+def main():
+    filelist = {}
 
-        for match in matches:
-            m = match
+    for page in sorted(PAGES.rglob("*.adoc")):
+        assembled = expand_partials(page.read_text(), set())
+        missing = find_unresolved(assembled)
 
-            if match[0] == "":
-                m = match[1]
-            else:
-                m = match[0]
+        if missing:
+            filelist[str(page.relative_to(ROOT))] = missing
 
-            if content.count("#" + m) != 1:
-                if file not in filelist:
-                    filelist[file] = list()
+    print("Files:", len(filelist))
 
-                filelist[file].append(m)
+    for file in filelist:
+        print(file)
 
+        for match in filelist[file]:
+            print("\t", match)
 
-print("Files:", len(filelist))
+    sys.exit(1 if filelist else 0)
 
-for file in filelist.keys():
-    print(file)
 
-    for match in filelist[file]:
-        print("\t", match)
+if __name__ == "__main__":
+    main()

+ 5 - 6
docs/modules/ROOT/pages/action_examples/ssh-manual.adoc

@@ -3,7 +3,7 @@
 
 include::partial$action_examples/ssh_intro.adoc[]
 
-NOTE: There is an easy method of setting up SSH with OliveTin, which is described in the <<action-ssh-easy,SSH (easy setup)>> section. This section is for those who want to set up SSH manually.
+NOTE: There is an easy method of setting up SSH with OliveTin, which is described in the xref:action_examples/ssh-easy.adoc[SSH (easy setup)] section. This section is for those who want to set up SSH manually.
 
 :systemd: Easy
 :container: Needs some setting up - see the <<ssh-container,SSH Container setup instructions>>
@@ -39,7 +39,7 @@ The steps in detail are below;
 [#ssh-step-1]
  [red]#Step 1#: Give OliveTin a SSH key
 
-Open a terminal window on _server-with-olivetin_. 
+Open a terminal window on _server-with-olivetin_.
 
 [loweralpha]
 . Create the `/opt/OliveTinSshKeys` directory, to create a shared directory for your SSH key file.
@@ -49,7 +49,7 @@ Open a terminal window on _server-with-olivetin_.
 root@server-with-olivetin: mkdir /opt/OliveTinSshKeys
 ----
 +
-This will later be used as a "volume mount" when you create a docker container. 
+This will later be used as a "volume mount" when you create a docker container.
 
 . Run `ssh-keygen` to generate a SSH key just for OliveTin.
 +
@@ -90,7 +90,7 @@ The key's randomart image is:
 +----[SHA256------+
 ----
 +
-This will create two files, `/opt/OliveTinSshKeys/id_rsa` (your private key) and `/opt/OliveTinSshKeys/id_rsa.pub` (your public key). 
+This will create two files, `/opt/OliveTinSshKeys/id_rsa` (your private key) and `/opt/OliveTinSshKeys/id_rsa.pub` (your public key).
 
 . Copy your public key to every server you want to connect to.
 +
@@ -113,7 +113,7 @@ root@server-with-olivetin: ssh -i /opt/OliveTinSshKeys/id_rsa root@server2
 (you should login without a password)
 ----
 
-. Give the SSH key to the OliveTin container. 
+. Give the SSH key to the OliveTin container.
 +
 The way to do this is via a "volume mount". When you create the container, you use "-v" to specify a volume.
 +
@@ -156,4 +156,3 @@ actions:
      icon: ping
      timeout: 5
 ....
-

+ 2 - 2
docs/modules/ROOT/pages/api/method_StartActionByGet.adoc

@@ -1,6 +1,6 @@
 = API Method: StartActionByGet
 
-This is the method that allows you to specify the action ID in the URL, and is probably the best to do quick integrations - QR Codes, streamdeck, etc. You cannot pass arguments using this method. 
+This is the method that allows you to specify the action ID in the URL, and is probably the best to do quick integrations - QR Codes, streamdeck, etc. You cannot pass arguments using this method.
 
 * **HTTP Method**: `GET`
 * **Request Type**: Action ID in the URL
@@ -8,6 +8,7 @@ This is the method that allows you to specify the action ID in the URL, and is p
 
 include::partial$api/start_action_methods.adoc[]
 
+[#api-eg-startIdUrl]
 == Example API call; Start an action by ID in the URL
 
 .curl
@@ -26,4 +27,3 @@ actions:
 ----
 
 IDs are used by these API calls, as you probably want the interface to display a human-readable title, whereas the API call doesn't want to have spaces or punctuation.
-

+ 1 - 3
docs/modules/ROOT/pages/api/start_action.adoc

@@ -13,7 +13,7 @@ Used by:
 *** xref:api/method_StartActionByGet.adoc[StartActionByGet]
 *** xref:api/method_StartActionByGetAndWait.adoc[StartActionByGetAndWait]
 
-If you are trying to integrate OliveTin with your own scripts or processes, it's probably easiest to start actions by using an ID directly in the URL, <<api-eg-startIdUrl,see the example>>.
+If you are trying to integrate OliveTin with your own scripts or processes, it's probably easiest to start actions by using an ID directly in the URL, xref:api/method_StartActionByGet.adoc#api-eg-startIdUrl[see the example].
 
 [#api-request-obj]
 == Request type: OliveTin request object
@@ -101,5 +101,3 @@ Used by:
     }
 }
 ----
-
-

+ 1 - 2
docs/modules/ROOT/partials/action_execution/onfileindir_arguments.adoc

@@ -13,5 +13,4 @@
 | `fileisdir`           | false
 |===
 
-Like all arguments, OliveTin also passes these arguments as <<env-vars,environment variables>> if this is better for your use case.
-
+Like all arguments, OliveTin also passes these arguments as xref:args/env.adoc[environment variables] if this is better for your use case.

+ 1 - 3
docs/modules/ROOT/partials/container_socket.adoc

@@ -69,6 +69,4 @@ xref:reference/containerInstallPackages.adoc[How to install additional packages
 +
 NOTE: The reason that the `arm` and `arm64` containers do not include docker, is that when these images are cross-compiled at build time, it takes FOREVER because we have to emulate arm.
 
-After you have passed the socket into the container (and optionally installed docker), you should be able to setup docker actions like it's shown in the example <<example-control-containers,above>>.
-
-
+After you have passed the socket into the container (and optionally installed docker), you should be able to setup docker actions like it's shown in the xref:action_examples/containers.adoc#example-control-containers[container start/stop example].